A friend asked me how to repair the vue template, the order of the class name, attributes of the order and the options attributes in the order of the problem, use eslint can do?

The answer is yes, but you need to write an ESLint plugin to check and fix it.

Given that he probably hasn’t written an ESLint plugin, I’ll take the relatively simple checksum fix order of class names and implement it.

Thought analysis

First, can ESLint parse the vue template?

This is possible because esLint’s Parser supports switching and Vue implements the corresponding parser, so you can use vue-esLint-Parser to parse templates in ESLint.

We can use AstExplorer.net to take a look at the AST generated by Parse.

We’re dealing with the class attribute, which is the value part of the VAttribute

You can pass in a comparator to customize the order and then set it back.

Of course, vue templates support {} to reference data, which we do not deal with, can be filtered out.

The idea is relatively simple, let’s write code to achieve it.

Code implementation

We can name the plug-in vue-class-order.

First, we’ll introduce esLint, set useEslintrc to false (i.e., not using configuration files), and then fill in the rules for the overrideConfig property to set various configurations.

const { ESLint } = require("eslint");

const engine = new ESLint({
    fix: false.overrideConfig: {
        parser: 'vue-eslint-parser'.parserOptions: {
            sourceType: 'module'
        },
        rules: {
            "vue-class-order": [ "error"]}},rulePaths: ['/'].useEslintrc: false
});
Copy the code

Parser needs to be vue-eslint-parser, and rulePaths (finding rule paths) need to be set. If fix is set to false, automatic repair is not performed.

After that, its lintText method is called to lint the code.

(async function main() {
  const results = await engine.lintText(` 
        `);

  console.log(results[0].output);

  const formatter = await engine.loadFormatter("stylish");
  const resultText = formatter.format(results);
  console.log(resultText); }) ();Copy the code

Print the fixed results and formatted errors.

The next step is to implement the plugin. Our goal is to examine the VAttribute node and use a custom comparator to sort the class.

First, add a parameter to the rule configuration:

rules: {
    "vue-class-order": [ "error".function(name1, name2) {
        return name1.charCodeAt(0) - name2.charCodeAt(0); }}]Copy the code

Then take it out from the plugin:

module.exports = {
     meta: {
         fixable: true
     },
     create(context) {
         const comparator = context.options[0]; }};Copy the code

The comparator is the parameter taken from the context.

The structure of the plug-in is meta, and the create part is the main logic of the plug-in. Meta is the meta information that describes the plug-in itself.

The create section returns a visitor that declares what to do on what node. But since the parser we use is vUE custom (vue-eslint-parser), we need the visitor to do the same:

module.exports = {
     meta: {
         fixable: true
     },
     create(context) {
         const comparator = context.options[0];
         return context.parserServices.defineTemplateBodyVisitor({
            "VAttribute[key.name=class]"(node) { } }); }};Copy the code

In the context. ParserServices. DefineTemplateBodyVisitor method introduced into specific visitor, for example, we need to do to VAttribute node processing.

Eslint supports the esqury notation, which allows you to specify which nodes to handle using a selector. Here we specify a VAttribute node with key.name as class

And then you have to get the value of the node, sort it, see if it’s correct, and if it’s not, you get an error.

"VAttribute[key.name=class]"(node) {  
    const classStr = node.value.value;
    if(! classStr.includes('{')) { // Filter out classes with interpolation expressions
        const curOrder = classStr.split(/\s/);
        const shouldOrder = [...curOrder].sort(comparator);
        if (curOrder.some((item, index) = >curOrder[index] ! == shouldOrder[index])) { context.report({ node,message: 'className out of order: ' + classStr,
                loc: node.value.loc }); }}}Copy the code

Thus, we implement lint for the order of classes in the Vue template.

Let’s try it out:

We implemented lint for className order!

Of course, just reporting errors without fixing is more rogue, we also have to implement automatic repair.

The fix is to replace the part of the value by taking the range of the value (the starting and ending subscripts) and replacing the text of that range using Fixer’s API. (Consider quotes here)

context.report({
    node,
    message: 'className out of order: ' + classStr,
    loc: node.value.loc,
    *fix(fixer) {
        const [ start, end ] = node.value.range;
        yield fixer.replaceTextRange([start + 1, end - 1], shouldOrder.join(' '))}});Copy the code

Let’s set fixer to true and run again:

Do automatic repair, no error!

We have implemented a class order check and automatic repair in the Vue template!

conclusion

Eslint can do code formatting checks and fixes based on the AST.

An AST requires a parser. Eslint supports extensions to parser, so there are many ESLint parsers available. To parse a Vue template, use vue-esLint-Parser. This can be viewed visually with AstExplorer.net.

We need to check the order of classes in the Vue template. After analysis, we need to fetch the value of the VAttribute node whose key is class, and then sort it according to the passed comparator. If the order is inconsistent, an error will be reported. It can also be fixed automatically via Fixer’s API by replacing the text in the range.

Here we call ESLint through the API, as well as through the CLI.

This article implements a relatively simple ESLint plugin, checking and fixing the format of the code in the Vue template. It is hoped that this article will help clarify the thinking of esLint plugin development.