fix()

in src/dispatch/static/dispatch/eslint-local-rules.js [89:386]


              fix(fixer) {
                const fixes = []

                // remove vee-validate imports
                vvImportNodes.forEach((node) => {
                  fixes.push(fixer.remove(node))
                  context.getDeclaredVariables(node).forEach((variable) => {
                    variable.references.forEach((reference) => {
                      fixes.push(
                        fixer.removeRange([
                          context.sourceCode.getIndexFromLoc({
                            line: reference.identifier.parent.loc.start.line,
                            column: 0,
                          }) - 1,
                          context.sourceCode.getIndexFromLoc(reference.identifier.parent.loc.end) +
                            1,
                        ])
                      )
                    })
                  })
                })
                vvRuleImportNodes.forEach((node) => {
                  fixes.push(fixer.remove(node))
                })

                if (observerNode) {
                  const observerRef = observerNode.startTag.attributes.find((attr) => {
                    return attr.type === "VAttribute" && attr.key.name === "ref"
                  })
                  const observerSlot = observerNode.startTag.attributes.find((attr) => {
                    return (
                      attr.type === "VAttribute" &&
                      attr.key.type === "VDirectiveKey" &&
                      attr.key.name.name === "slot"
                    )
                  })
                  const otherAttrs = observerNode.startTag.attributes
                    .filter((attr) => {
                      return attr !== observerRef && attr !== observerSlot
                    })
                    .map((attr) => context.sourceCode.getText(attr))

                  let formSubmitHandlerName
                  let formSubmitHandlerNode
                  if (formNode) {
                    const submitListener = formNode.startTag.attributes.find((attr) => {
                      return (
                        attr.directive &&
                        attr.key.name.name === "on" &&
                        attr.key.argument.name === "submit"
                      )
                    })

                    if (submitListener) {
                      formSubmitHandlerName = submitListener.value.expression.name
                      if (formSubmitHandlerName) {
                        methodNodes.forEach((node) => {
                          if (node.key.name === formSubmitHandlerName) {
                            formSubmitHandlerNode = node
                          }
                        })
                        if (!formSubmitHandlerNode) {
                          throw new Error("Unable to locate form submit handler")
                        }
                      }
                    } else {
                      throw new Error("No submit listener")
                    }
                  }

                  let newStartTag = "<v-form"
                  if (observerRef) {
                    newStartTag += ` ref="form"`
                  }
                  if (otherAttrs.length) {
                    newStartTag += " " + otherAttrs.join(" ")
                  }
                  if (formNode) {
                    const formAttrs = formNode.startTag.attributes.map((attr) =>
                      context.sourceCode.getText(attr)
                    )
                    if (formAttrs.length) {
                      newStartTag += " " + formAttrs.join(" ")
                    }
                  } else {
                    newStartTag += ` @submit.prevent`
                  }
                  if (observerSlot) {
                    newStartTag += ` v-slot="{ isValid }"`
                  }
                  newStartTag += ">"

                  if (formNode) {
                    fixes.push(
                      fixer.replaceTextRange(
                        [observerNode.startTag.range[0], formNode.startTag.range[1]],
                        newStartTag
                      )
                    )
                  } else {
                    fixes.push(fixer.replaceText(observerNode.startTag, newStartTag))
                  }

                  if (observerSlot) {
                    observerNode.variables.forEach((variable) => {
                      if (variable.id.name === "invalid") {
                        variable.references.forEach((ref) => {
                          fixes.push(fixer.replaceText(ref.id, "!isValid.value"))
                        })
                      } else if (variable.id.name !== "validated") {
                        throw new Error("unsupported variable")
                      }
                    })
                  }

                  if (formNode) {
                    fixes.push(
                      fixer.replaceTextRange(
                        [formNode.endTag.range[0], observerNode.endTag.range[1]],
                        "</v-form>"
                      )
                    )
                  } else {
                    fixes.push(fixer.replaceText(observerNode.endTag, "</v-form>"))
                  }

                  observerRefNodes.forEach((node) => {
                    fixes.push(fixer.replaceText(node.property, "form"))
                    if (node.parent.property.name === "reset") {
                      fixes.push(fixer.replaceText(node.parent.property, "resetValidation"))
                    } else {
                      fixes.push({
                        range: Array(2).fill(
                          context.sourceCode.getIndexFromLoc({
                            line: node.loc.start.line + 1,
                            column: 0,
                          }) - 1
                        ),
                        text: " // TODO: find vuetify equivalent",
                      })
                    }
                  })

                  if (formSubmitHandlerNode) {
                    if (!formSubmitHandlerNode.value.async) {
                      fixes.push(fixer.insertTextBefore(formSubmitHandlerNode.key, "async "))
                    }
                    let paramName = "event"
                    if (!formSubmitHandlerNode.value.params.length) {
                      fixes.push(
                        fixer.replaceTextRange(
                          [
                            formSubmitHandlerNode.value.range[0],
                            formSubmitHandlerNode.value.body.range[0],
                          ],
                          `(${paramName}) `
                        )
                      )
                    } else {
                      paramName = formSubmitHandlerNode.value.params[0].name
                    }
                    const indent = context.sourceCode.lines
                      .at(formSubmitHandlerNode.value.body.body[0].loc.start.line - 1)
                      .match(/^\s*/)[0]
                    fixes.push(
                      fixer.insertTextBefore(
                        formSubmitHandlerNode.value.body.body[0],
                        `if (!(await ${paramName}).valid) return\n\n${indent}`
                      )
                    )
                  }
                }

                validationProviderNodes.forEach(({ node, child, rules, vid }) => {
                  fixes.push(fixer.removeRange([node.startTag.range[0], child.startTag.range[0]]))

                  if (node.variables.length) {
                    const b4 = template.getTokenBefore(
                      node.variables[0].references[0].id.parent.parent
                    )
                    fixes.push(
                      fixer.removeRange([
                        b4.range[1],
                        node.variables[0].references[0].id.parent.parent.range[1],
                      ])
                    )
                  } else {
                    node.children.forEach((child) => {
                      if (child.type === "VElement") {
                        child.startTag.attributes.forEach((attr) => {
                          if (
                            attr.key.type === "VDirectiveKey" &&
                            attr.key.name.name === "slot-scope"
                          ) {
                            fixes.push(fixer.remove(attr))
                            if (child.variables.length) {
                              child.variables.forEach((variable) => {
                                const b4 = template.getTokenBefore(
                                  variable.references[0].id.parent.parent
                                )
                                fixes.push(
                                  fixer.removeRange([
                                    b4.range[1],
                                    variable.references[0].id.parent.parent.range[1],
                                  ])
                                )
                              })
                            } else {
                              throw new Error("slot-scope without variables")
                            }
                          }
                        })
                      }
                    })
                  }

                  const isMultiline = child.startTag.loc.start.line !== child.startTag.loc.end.line
                  const indent = isMultiline
                    ? "\n" +
                      context.sourceCode.lines
                        .at(child.startTag.loc.start.line - 1)
                        .match(/^\s*/)[0] +
                      " ".repeat(2)
                    : " "

                  if (vid) {
                    fixes.push(
                      fixer.insertTextAfter(
                        child.startTag.attributes.at(-1),
                        indent +
                          (vid.directive
                            ? `:name=${context.sourceCode.getText(vid.value)}`
                            : `name="${vid.value.value}"`)
                      )
                    )
                  }
                  if (rules) {
                    let rulesArray
                    let rulesString
                    if (rules.directive) {
                      // dynamic rules
                      if (
                        rules.value.expression.type !== "TemplateLiteral" ||
                        rules.value.expression.quasis.length !== 2 ||
                        rules.value.expression.expressions.length !== 1 ||
                        rules.value.expression.expressions[0].type !== "ConditionalExpression" ||
                        rules.value.expression.expressions[0].alternate.type !== "Literal" ||
                        rules.value.expression.expressions[0].alternate.value !== ""
                      ) {
                        throw new Error("Unsupported dynamic rules")
                      }
                      const test = context.sourceCode.getText(
                        rules.value.expression.expressions[0].test
                      )
                      const rulesValue = rules.value.expression.expressions[0].consequent.value
                      rulesArray = rulesValue.split("|")
                      rulesString = `:rules="${test} ? [${rulesArray
                        .map((v) => `rules.${v}`)
                        .join(", ")}] : []"`
                    } else {
                      const rulesValue = rules.value.value
                      rulesArray = rulesValue.split("|")
                      rulesString = `:rules="[${rulesArray.map((v) => `rules.${v}`).join(", ")}]"`
                    }
                    fixes.push(
                      fixer.insertTextAfter(child.startTag.attributes.at(-1), indent + rulesString)
                    )
                    rulesArray.forEach((rule) => rulesToImport.add(rule))
                  }

                  fixes.push(
                    fixer.removeRange([
                      child.startTag.selfClosing ? child.startTag.range[1] : child.endTag.range[1],
                      node.endTag.range[1],
                    ])
                  )
                })

                if (rulesToImport.size) {
                  fixes.push(
                    fixer.insertTextBefore(
                      context.sourceCode.ast.body[0],
                      "import { " + [...rulesToImport].join(", ") + " } from '@/util/form'\n"
                    ),
                    fixer.insertTextAfterRange(
                      [vueObjectNode.range[0] + 1, vueObjectNode.range[0] + 1],
                      `
  setup() {
    return {
      rules: { ${[...rulesToImport].join(", ")} }
    }
  },`
                    )
                  )
                }

                return fixes
              },