function wrapRuleModule(baseRule, ruleName, options) {
  let description = baseRule.meta.docs.description
  if (description) {
    description += ' in `<template>`'
  }

  const {
    categories,
    skipDynamicArguments,
    skipDynamicArgumentsReport,
    skipBaseHandlers,
    applyDocument,
    preprocess,
    create
  } = options || {}
  return {
    create(context) {
      const sourceCode = context.getSourceCode()
      const tokenStore =
        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`.
      if (tokenStore) {
        context = wrapContextToOverrideTokenMethods(context, tokenStore, {
          applyDocument
        })
      }

      if (skipDynamicArgumentsReport) {
        context =
          wrapContextToOverrideReportMethodToSkipDynamicArgument(context)
      }

      /** @type {TemplateListener} */
      const handlers = {}

      if (preprocess) {
        preprocess(context, {
          wrapContextToOverrideProperties(override) {
            context = newProxy(context, override)
            return context
          },
          defineVisitor(visitor) {
            compositingVisitors(handlers, visitor)
          }
        })
      }

      const baseHandlers = baseRule.create(context)
      if (!skipBaseHandlers) {
        compositingVisitors(handlers, baseHandlers)
      }

      // Move `Program` handlers to `VElement[parent.type!='VElement']`
      if (handlers.Program) {
        handlers[
          applyDocument
            ? 'VDocumentFragment'
            : "VElement[parent.type!='VElement']"
        ] = /** @type {any} */ (handlers.Program)
        delete handlers.Program
      }
      if (handlers['Program:exit']) {
        handlers[
          applyDocument
            ? 'VDocumentFragment:exit'
            : "VElement[parent.type!='VElement']:exit"
        ] = /** @type {any} */ (handlers['Program:exit'])
        delete handlers['Program:exit']
      }

      if (skipDynamicArguments) {
        let withinDynamicArguments = false
        for (const name of Object.keys(handlers)) {
          const original = handlers[name]
          /** @param {any[]} args */
          handlers[name] = (...args) => {
            if (withinDynamicArguments) return
            // @ts-expect-error
            original(...args)
          }
        }
        handlers['VDirectiveKey > VExpressionContainer'] = () => {
          withinDynamicArguments = true
        }
        handlers['VDirectiveKey > VExpressionContainer:exit'] = () => {
          withinDynamicArguments = false
        }
      }

      if (create) {
        compositingVisitors(handlers, create(context, { baseHandlers }))
      }

      if (applyDocument) {
        // Apply the handlers to document.
        return defineDocumentVisitor(context, handlers)
      }
      // Apply the handlers to templates.
      return defineTemplateBodyVisitor(context, handlers)
    },

    meta: Object.assign({}, baseRule.meta, {
      docs: Object.assign({}, baseRule.meta.docs, {
        description,
        category: null,
        categories,
        url: `https://eslint.vuejs.org/rules/${ruleName}.html`
      })
    })
  }
}
Wrap a given rule to apply it to Vue.js template.
@param {RuleModule} baseRule The rule implementation to wrap.
@param {string} ruleName The name of the wrapped rule.
@param {WrapRuleOptions} [options] The option of this rule.
@returns {RuleModule} The wrapped rule implementation.