Writing local rules for ESLint
Tags: javascript, programming, webdev, beginners
Published: 2022-05-23 13:27:30

ESLint is a great tool that allowed me to write better code by following the already defined principles for clean code and syntax. However, you may run into situations where the already published ESLint packages and rules will not work (i.e. you will have a code that would need to be replaced due to some business requirements). This is exactly the case that happened to me few weeks ago. 

I needed to create an ESLint rule that would basically replace one string (import of the package) with another (custom defined by me). Seems like a relatively simple task. So I did exactly what probably all developers would do at this point. I typed "ESLint replace import with something else" and I was shocked that I could not find any resources that would help me. Maybe it is just me not being able to google properly or the appropriate article or documentation was created already but I had to create the following code all by myself by guessing (JavaScript did not help).

So, below you will see a code sample that is a local ESLint rule that will allow you to replace one import statement with another (defined by you).

![Meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v0dgpmboenc1f18q3now.png)
 

## Code

First, let's install a package that would allow us to write local rules:


```bash
yarn add --dev eslint-plugin-local-rules # npm install --save-dev eslint-plugin-local-rules
```

It will allow us to write local rules without a need to publish them as npm packages.

Next, let's add it to the plugins array

```js
// .eslintrc.js

module.exports = {
  plugins: ['eslint-plugin-local-rules'],
};
```

Now, into the local rule itself. This is the biggest part of the tutorial code so I will try to explain each section step by step so that you will learn what I had to verify myself by guessing :D. At the end you will see a full file with all necessary declarations for your local rule to work.

First, at the top of the file you will see a `module.exports` and inside of it a string key called `replace-bad-import`. This is the name of your local rule that will be needed in the eslintrc file later.

```js
// eslint-local-rules.js

module.exports = {
  'replace-bad-import': {},
};
```

In the meta configuration, let's define information about our local module like description, category, etc. This is more informative way so it is not that important for now.

```js
// eslint-local-rules.js

module.exports = {
  'replace-bad-import': {
    meta: {
      fixable: "code",
      docs: {
        description: 'My awesome ESLint local rule that will replace an import declaration with something else',
        category: 'Possible Errors',
        recommended: false,
      },
      schema: [],
    },
  },
};
```

Now, into the final part of the local rule which is the `create` method:

```js
// eslint-local-rules.js

module.exports = {
  'replace-bad-import': {
    create(context) {
      return {
        ImportDeclaration(node) {
          if(node.source.value.includes('bad-import-declaration')) {
            context.report({
              node,
              message: 'Use proper import',
              fix: fixer => fixer.replaceText(node, node.specifiers.map(specifier =>`import ${specifier.local.name} from 'good-import-declaration';`,).join('\n'))
            });
          }
        },
      };
    },
  },
};
```

Let's take a look at all things here step by step:
1. `create` method will accept a `context` parameter that will be later used to create a report about a found issue.
2. This method will return a new `ImportDeclaration` rule. If you are interested in other rules check out official [docs](https://eslint.org/docs/rules/)
3. We are checking if a certain node import contains a query (in our case `bad-import-declaration`)
4. Inside this if statement we are generating a new report by calling a method from the context object with the following parameters:

* `node`: actual node (something like stack trace) place that triggered the rule
* `message`: a message that should be displayed after running a rule and finding the issue
* `fix`: a fixer method that will be used to fix the import statement. In this case it is a method that uses a `fixer` as a param and then this `fixer`'s method called `replaceText` is called with a current node and a new value that should be added instead the node.

Below, you can see the full code of the rule:

```js
// eslint-local-rules.js

module.exports = {
  'replace-bad-import': {
    meta: {
      fixable: "code",
      docs: {
        description: 'My awesome ESLint local rule that will replace an import declaration with something else',
        category: 'Possible Errors',
        recommended: false,
      },
      schema: [],
    },
    create(context) {
      return {
        ImportDeclaration(node) {
          if(node.source.value.includes('bad-import-declaration')) {
            context.report({
              node,
              message: 'Use proper import',
              fix: fixer => fixer.replaceText(node, node.specifiers.map(specifier =>`import ${specifier.local.name} from 'good-import-declaration';`,).join('\n'))
            });
          }
        },
      };
    },
  },
};
```

The final step here is to add our rule to the `.eslintrc.js` file.

```js
// .eslintrc.js

module.exports = {
  rules: {
    'local-rules/replace-bad-import': 'warn',
  },
};
```

If we implemented everything correctly following line:

![Bad Import Declaration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/srm69bq41zqggzhyf6jh.png)

Should be replaced and look like following:

![Good Import Declaration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1b1xc16jpbg4x4v2esyd.png)

## Summary

Well done! You have just created a local ESLint rule that will replace one text with another. Keep in mind that it is just a beginning of the power of ESLint but this should give you a solid start in terms of building local rules.