How to create a custom ESlint plugin
Tags: javascript, react, node, eslint
Published: 2022-03-16 11:45:06

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:

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

<button onclick=”onLogin()”>Login</button>
function onLogin() {}
```

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](https://eslint.org/docs/developer-guide/)


#### 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
```
#### Step 2:
Initialize the Node.js project using the following command from the terminal
```
npm init
```
My package.json looks like this.
> package.json:
```json
{
  "name": "my-lint",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
```
#### Step 3:
It’s time to add ESLint dependencies
```cmd
npm install -D eslint
```
Add eslint script to the package.json
```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"
}
```
#### 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:
```javascript
module.exports = {
  parserOptions: {
    ecmaVersion: 2018,
  },
  extends: ["eslint:recommended"],
  plugins: [],
  rules: {},
};
```
#### 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
```javascript
const someFunction = () => {
    console.log("Hello World");
};
someFunction();
```

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)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9x62x6fe4ovcsozwhxzl.png "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
```

#### Step 2:
Create an index.js file inside the packages/eslint directory.
```javascript
const {onFuncPrefixMatchingCreate}= require("./funcPrefixMatching");
module.exports = {
  rules: {
    "func-prefix-matching": {
      create: onFuncPrefixMatchingCreate,
    },
  },
};
```
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:
```javascript
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 };
```

#### Step 4:
It’s time to add our ESLint plugin to the dependencies
```
npm i file:packages/eslint -D
```
#### Step 5:
Update .eslintrc.js
```javascript
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.
  },
};
```

#### Step 6:
Time to check our plugin.

![How to create a custom ESlint plugin | Rahul Sharma(DevsMitra)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ii2l7dh9sbtlqbzep228.png "How to create a custom ESlint plugin | Rahul Sharma(DevsMitra)")


> Custom prefix
```javascript
module.exports = {
  parserOptions: {
    ecmaVersion: 2018,
  },
  extends: ["eslint:recommended"],
  plugins: ["my-lint"],
  rules: {
    "my-lint/func-prefix-matching": 
    [ 1, { 
           include: ["to"], 
           exclude: ["excludeSomeFunction"], 
           message: ""
         }
    ]
  },
};
```
> index.js
```javascript
// Custom include
const toSomeFunction = () => {
  console.log("Hello");
};
// Custom exclude
const excludeSomeFunction = () => {
  console.log("Hello");
};
toSomeFunction();
excludeSomeFunction();
```
Working without error. 😎

![How to create a custom ESlint plugin | Rahul Sharma(DevsMitra)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f8llhl4gmgfldk07j8b4.png "How to create a custom ESlint plugin | Rahul Sharma(DevsMitra)")


**Repo:** [func-prefix-matching](https://github.com/devsmitra/func-prefix-matching)

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

--- 
### Must Read If you haven't

{% link https://dev.to/devsmitra/no-more-redux-action-creators-functions-30j %}

{% link https://dev.to/devsmitra/how-to-cancel-javascript-api-request-with-abortcontroller-36cm %}

{% link https://dev.to/devsmitra/13-typescript-utility-a-cheat-sheet-for-developer-ab3 %}

{% link https://dev.to/devsmitra/how-to-solve-rest-api-routing-problem-with-decorators-5b3n %}

{% link https://dev.to/devsmitra/3-steps-to-create-custom-state-management-library-with-react-hooks-and-context-api-1bfh %}


### Catch me on
{% user devsmitra %}
<p>
{% cta https://www.youtube.com/@devsmitra?sub_confirmation=1 %} Youtube {% endcta %}
{% cta https://github.com/devsmitra %} Github {% endcta %}
{% cta https://www.linkedin.com/in/rahul-sharma-18b8b374 %} LinkedIn {% endcta %}
{% cta https://devsmitra.medium.com %} Medium {% endcta %}
{% cta https://stackblitz.com/@devsmitra %} Stackblitz {% endcta %} {% cta https://devsmitra.hashnode.dev %} Hashnode {% endcta %}
{% cta https://hackernoon.com/u/devsmitra %} HackerNoon {% endcta %}
</p>