DEV Community

Gabriele Buffolino
Gabriele Buffolino

Posted on • Originally published at bite-sized.hashnode.dev on

Custom ESLint rules #3/5: creating a new rule

To save our teammates from eating pineapple pizza, we decided to create a custom ESLint plugin with a dedicated rule. In the previous bite, we set up a sample monorepo and now it's time to create our rule!

How to write a custom ESLint rule

At this point, we need to define our custom ESLint ruleset. We will focus on eslint-plugin-pizza folder.

We have to decide our rule name. According to naming conventions, we should use something like no-pineapple-pizza. Let's go for it and create all the relevant files.

yummy-pizza $ touch packages/eslint-plugin-pizza/rules/no-pineapple-pizza.js packages/eslint-plugin-pizza/index.js

Enter fullscreen mode Exit fullscreen mode

This is what we have:

yummy-pizza/
 packages/
   eslint-plugin-pizza/
     rules/
       no-pineapple-pizza.js
     package.json
     index.js
   app/
     src/
     package.json
 package.json

Enter fullscreen mode Exit fullscreen mode
  • rules/no-pineapple-pizza.js is our rule

  • index.js is where our rules are exported

It is time to write our rule! To create it, my suggestion is to take advantage of the Abstract Syntax Tree Explorer tool and understand what is the best way to match our pineapple code. Also, we need to provide some metadata to our rule. For the detailed list of metadata to use, refer to the official docs.

In our example, we want to match the eatPineapplePizza function. If we feed AST Explorer with a sample code, it will tell us how to build our rule. Place the cursor upon the incriminated text and see what is highlighted on the right-hand side. For a way more detailed guide on how to use AST, you can check this blog post that I found extremely helpful.

AST example

So after playing a bit with it, I ended up with the following code:

// yummy-pizza/packages/eslint-plugin-pizza/rule/no-pineapple-pizza.js

const rule = {
  meta: {
    type: "problem", // our rule is detecting an error
    fixable: "code", // our rule can be automatically fixed
    docs: {
      description: "Don't eat pineapple pizza.", // short rule description
      category: "Pizza ruleset",
    },
  },
  create: function (context) {
    return {
      // this code is generated using AST
      CallExpression(node) {
        if (
          node.callee.type === "Identifier" &&
          node.callee.name === "eatPineapplePizza"
        ) {
          context.report({
            node,
            // message to display to pineapple pizza eaters
            message:
              "A notification was sent to the Italian police. They're coming. Pineapple pizza is illegal, please eat pizza Margherita instead",
            fix(fixer) {
            // we provide an automatic fix: replace 'usePineapplePizza' with 'usePizzaMargherita'
              return fixer.replaceText(node.callee, "eatPizzaMargherita");
            },
          });
        }
      },
    };
  },
};

export default rule;

Enter fullscreen mode Exit fullscreen mode

Finally, edit index.js file to export the new rule.

// yummy-pizza/packages/eslint-plugin-pizza/index.js

import noPineapplePizzaRule from "./rules/no-pineapple-pizza"

const plugin = {
  rules: {
    "no-pineapple-pizza": noPineapplePizzaRule,
  },
};

export default plugin;

Enter fullscreen mode Exit fullscreen mode

Our rule is ready!

In the next bite, we'll learn how to test it to ensure it works properly.

References

Top comments (0)