DEV Community

Cover image for How to create a custom ESlint plugin
Rahul Sharma
Rahul Sharma

Posted on • Edited on

How to create a custom ESlint plugin

Recently our development team decided to enforce the role for function naming. Meaning, all function name should start with the prefix defined in the eslint rule.
Instead of giving some random names to the function, strict developers follow some pre-defined conventions.

Example:

<button onclick=someFunction()>Login</button>
function someFunction() {}

<button onclick=onLogin()>Login</button>
function onLogin() {}
Enter fullscreen mode Exit fullscreen mode

In the HTML button click, we can give any name we want. but when we read the code, the second function makes more sense. Let’s write a plugin that will warn us about wrong function naming.

JavaScript Naming Convention Best Practices

Boolean: is, are, has
Events: init, pre, on, post
verb as a prefix: get, set, post, put, push, apply, calculate, compute, to, etc.

We’ll be using eslint for this project.

ESLint Introduction:

ESLint statically analyzes your code to quickly find problems. ESLint is built into most text editors and you can run ESLint as part of your continuous integration pipeline.

Definition referred from eslint official site, Check more details about ESlint here

Prerequisite:

  • Make sure you have installed the latest Node.js ( Download link )
  • Basic knowledge of javascript, eslint
  • Code editor (vs code)

There are 2 sections to this article in the

Section 1. We’ll be creating a Node.js project and setup eslint for it. If you have already created a project and eslint setup. you can skip this section and directly jump into section 2.
Section 2. We’ll create an eslint plugin and use that in the project.

Section 1: Setup Node.js project

Step 1:

Open the terminal and run the following command

mkdir my-lint //creating directory
cd my-lint
Enter fullscreen mode Exit fullscreen mode

Step 2:

Initialize the Node.js project using the following command from the terminal

npm init
Enter fullscreen mode Exit fullscreen mode

My package.json looks like this.

package.json:

{
  "name": "my-lint",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
Enter fullscreen mode Exit fullscreen mode

Step 3:

It’s time to add ESLint dependencies

npm install -D eslint
Enter fullscreen mode Exit fullscreen mode

Add eslint script to the package.json

{
  "name": "my-lint",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "lint": "eslint src/**/*.js"
  },
  "devDependencies": {
    "eslint": "^7.21.0"
  },
  "author": "",
  "license": "ISC"
}
Enter fullscreen mode Exit fullscreen mode

Step 4:

Next, We have to create a .eslintrc.js with the following configuration - this is similar to what you already do in your apps:

module.exports = {
  parserOptions: {
    ecmaVersion: 2018,
  },
  extends: ["eslint:recommended"],
  plugins: [],
  rules: {},
};
Enter fullscreen mode Exit fullscreen mode

Step 5:

Create src and packages directories in the root directory.

Create a src/index.js file inside the src directory and add the following code to it.

index.js

const someFunction = () => {
    console.log("Hello World");
};
someFunction();
Enter fullscreen mode Exit fullscreen mode

You can run the npm run lint or yarn lint to check eslint working or not.
So far we have created the Node.js project and setup eslint for it.

How to create a custom ESlint plugin project | Rahul Sharma(DevsMitra)

Section 2: Create eslint plugin

An ESLint rule contains 2 main parts:

  • meta: an object where we will specify the usage of our rule.
  • create: a function that will return an object with all the methods that ESLint will use to parse our statement. Each method returned is an AST(Abstract Syntax Tree) node.

NOTE: Plugin should follow name format of eslint-plugin-<plugin-name>

Let’s get started…

Step 1:

Create a eslint directory in your packages directory.

Run the following command in your terminal

cd packages/eslint
npm init // I'm giving project name to eslint-plugin-my-lint
Enter fullscreen mode Exit fullscreen mode

Step 2:

Create an index.js file inside the packages/eslint directory.

const {onFuncPrefixMatchingCreate}= require("./funcPrefixMatching");
module.exports = {
  rules: {
    "func-prefix-matching": {
      create: onFuncPrefixMatchingCreate,
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Don’t worry about funcPrefixMatching import will be creating that in the next step.

Step 3:

Create a javascript file called funcPrefixMatching.js in your packages/eslint directory, put the below code in it:

const rulePrefix = ["is", "pre", "on", "post", "get", "set"];
const isValidName = (name, { prefix, exclude }) => {
  const isValid = (prefix) => name.indexOf(prefix) === 0;
  return exclude.some(isValid) || prefix.some(isValid);
};
const onFuncPrefixMatchingCreate = (context) => {
  const { options } = context;
  const {include = [], exclude = [] } = options[0]||{};
  return {
    Identifier: (node) => {
      if (node.parent.init && 
        node.parent.init.type === "ArrowFunctionExpression"
        // You can add more checks here
      ) {

      const { name } = node;
      const allPrefix = [...include, ...rulePrefix].sort();
      // Sorting is optional

      if (!isValidName(name, { prefix: allPrefix, exclude })) {
        context.report(node, `${name} should start with ${allPrefix.join(", ")}.`);
      }
     }
    },
  };
};
module.exports = { onFuncPrefixMatchingCreate };
Enter fullscreen mode Exit fullscreen mode

Step 4:

It’s time to add our ESLint plugin to the dependencies

npm i file:packages/eslint -D
Enter fullscreen mode Exit fullscreen mode

Step 5:

Update .eslintrc.js

module.exports = {
  parserOptions: {
    ecmaVersion: 2018,
  },
  extends: ["eslint:recommended"],
  plugins: ["my-lint"],
  rules: {
    "my-lint/func-prefix-matching": 
    [ 1, { include: [], exclude: [] } ] 
    // We can include or exclude prefix
    // You can add more option here like message, ignore case etc.
  },
};
Enter fullscreen mode Exit fullscreen mode

Step 6:

Time to check our plugin.

How to create a custom ESlint plugin | Rahul Sharma(DevsMitra)

Custom prefix

module.exports = {
  parserOptions: {
    ecmaVersion: 2018,
  },
  extends: ["eslint:recommended"],
  plugins: ["my-lint"],
  rules: {
    "my-lint/func-prefix-matching": 
    [ 1, { 
           include: ["to"], 
           exclude: ["excludeSomeFunction"], 
           message: ""
         }
    ]
  },
};
Enter fullscreen mode Exit fullscreen mode

index.js

// Custom include
const toSomeFunction = () => {
  console.log("Hello");
};
// Custom exclude
const excludeSomeFunction = () => {
  console.log("Hello");
};
toSomeFunction();
excludeSomeFunction();
Enter fullscreen mode Exit fullscreen mode

Working without error. 😎

How to create a custom ESlint plugin | Rahul Sharma(DevsMitra)

Repo: func-prefix-matching

Got any questions or additional? please leave a comment.
Thank you for reading 😊


Must Read If you haven't

Catch me on

Youtube Github LinkedIn Medium Stackblitz Hashnode HackerNoon

Top comments (2)

Collapse
 
arthurka profile image
ArthurKa

Hi. What about types and TypeScript for plugin creation? Does it have any type declaration?

Collapse
 
arthurka profile image
ArthurKa

Figured out.