Creating Custom Eslint Rules

How to use Eslint like a pro

Babatunde Koiki
Better Programming

--

A gavel
Photo by Tingey Injury Law Firm on Unsplash

Introduction

ESLint is a static code analysis tool for identifying problematic patterns found in JavaScript code. Nicholas C. Zakas created it in 2013. ESLint rules are configurable, and customised rules can be defined and loaded.

ESLint covers both code quality and coding style issues — Wikipedia. In simple terms, ESLint is a code analysis tool used for code formatting, error checking, enforcing code styles, among others. It is used to enforce code styles across a project. There are a couple of standard code styles in ESLint. These formats and styles are referred to as rules.

This article will explain how to create your own custom ESLint rules and create a script with the ESLint module that will be used to lint files.

Project Setup

We’ll be creating a simple TypeScript project. It will be barebone, just a couple of files to test our custom rules.

Run the following commands to set up the typescript project:

npm init –ytsc — init

These commands will create package.json and tsconfig.json files for us.

Replace the tsconfig.json file with the following:

Next, let’s set up ESLint for our project.

Install ESLint for your project with the following command:

npm install eslint — save-dev

Next, set up ESLint for the project with the following command:

npm init @eslint/config

Follow the prompt and answer the questions like the illustration below:

Our eslintrc.js file should look like the one below:

Now our project is completely set up, and we can compile any file that we add and lint those files.

Creating Our Linting Script

Next, we need to create the script that we will be using to lint our files. We’ll be using the eslint module to do this magic from our node script rather than running the eslint. from our terminal. We’ll be using the glob package for getting files in this script, so we need to install it. You can run the following command to install it:

npm i glob

We need to install some type of definitions also. Run the following command to install it:

npm i -D @types/eslint @types/node @types/glob

Now, we can create a file called lint_files.ts and insert the following inside it:

This file is pretty easy to understand. We have four helper functions used for changing the colour of our console. This way, we can add info, success, warning, and error logs where necessary.

The last function gets called when this script is called. Inside this function, first, we get all TypeScript and JavaScript files using the glob module. Next, we filter the files returned from glob and return only TypeScript and JavaScript files as these are the files we want to lint. Next, we create an eslint object.

According to the documentation, we pass some of the parameters we need, like extensions, errorOnUnmatchedPattern, and baseConfig, which is the eslintrc.js file (the ESLint configuration). We also pass another important parameter: the rulePaths. This option tells ESLint to look for that directory and check for any ESLint rules defined in that directory.

We loop through all the files then call the lintFiles method, which does the linting and fixes the issue if it can be fixed. We then loop through the result we got from linting, an array of lint results for each file that was linted. The message property is the error message object for each error in each file. The severity property of the message property can be 1 for warning or 2 for error.

Before running this script, we need to create the directory that we specified as the rulePaths. Create a folder called lint_rules in your root project and create any file inside it, then compile your code. If you run this script, you should see the following:

As we were expecting, we shouldn’t have any errors.

Creating Our First Rule

This section will focus on creating our first custom rule, but before we do, we need to understand how codes are structured. We do this by using AST explorer, Abstract Syntax Tree. AST explorer helps explain the breakdown of our code. Let’s look at the breakdown of the code sample below:

We’ll be using the AST explorer website, shown below:

If we point our mouse on any part of the code, we can transverse the node. Let’s take the printTips function, for instance.

We can access the printTips function declaration using the FunctionDeclaration node. We can access properties of the like type, id, name or even the parent node using the parent property.

Let’s take another example, say we have the code snippet below:

We can access any of the cases by using the SwitchCase node and checking that the node’s test.value property is what we want. For instance, if we’re working with the first case, we can use the SwitchCase node and check that the node.test.value is the same as hello.

Now, back to our custom rules. To create a custom rule, we will create a file inside the lint_rules folder that we created earlier. The file name should be the rule name. The rule that we will be creating in this article will disallow naming a function dangerous outside our project’s dangerous directory.

Create a file name no_dangerous_function.js inside the lint_rules directory and paste the following inside:

First, we created a helper function that takes in two parameters, context and node and checks if the file being linted is in the dangerous directory in our root project. If it is in this directory, we just exit the function, but if the file isn’t in the dangerous directory, we report an error context.report that takes some options. First is the node which is the node that has the error, the second is the message which will be displayed as the linting message, and fix is the function that can be used to fix it and is optional.

We also exported an object that is passed to ESLint’s meta describes the metadata of the rule. If you want the rule to be fixable by ESLint, you need to pass a fixable property to the meta, and it can either be code or whitespace.

The create property is a function that takes in the context object, and here is where we do the actual work. We have three kinds of nodes inside:

  1. CallExpression: This node checks when a function is called
  2. FunctionDeclaration: This node checks when a function is declared using the function keyword.
  3. ArrowFunctionExpression: This node checks when a function is declared using an arrow function.

We need these three nodes for our use case, as we want to be sure that a function named dangerous is not called or declared in any way outside the dangerous directory.

From the images above, we can see why we chose each node, and why we were able to access node.parent.id.name for ArrowFunctionExpression , node.id.name for FunctionDeclaration, and node.callee.name for CallExpression. Next, we need to enable this rule, to do this edit the rule property of your eslintrc.js file by adding this rule to it.

Add this as another property of the rule property of your eslintrc.js file:

no_dangerous_function: ‘error’,

Now, all your files should be giving you an error message like the one below:

This is because the ESLint extension of VSCode can not find this rule. We will fix that later. Now let’s test our rule. In your app.ts file, add the following:

export {}function dangerous () {    console.log(‘dangerous’)}dangerous()

If you lint the files again, you should get the following error message. And this is proof that even though our editor is giving an error message that it can’t see the rule, we added it to the eslintrc.js file, and we didn’t get a warning telling us not to name the function `dangerous` inside the app.ts file, so our rule is working perfectly well.

Let’s create a directory called dangerous and create a file inside it. After that, we can finally paste the same code in our app.ts file inside, as you can see below:

export {}function dangerous () {    console.log(‘dangerous’)}dangerous()

If we lint our file code again, only the app.ts file should raise an error.

Let’s create another file in the root directory of our project to test for arrow functions by creating a function named dangerous inside this file.

Configuring Our Editor and Eslint

To fix the warning “Definition for rule ‘no_dangerous_function’ was not found. eslint(no_dangerous_function),” we need to tell VSCode to check for custom rules in a particular directory. To do this, we need to create VSCode settings for our project. This is done by creating a settings.json file inside our project’s .vscode directory. Paste the following inside this file:

// Place your settings in this file to overwrite default and user settings.{    “eslint.options”: {        “rulePaths”: [“lint_rules”],    }}

All those errors should go away, and VSCode is also smart enough to warn us before linting our files.

Adding a Fixer for Our Rule

If we hover on this error and click on the fix button, we can only disable the rule. But this is not enough. We might want to make ESlint fix those issues for us automatically for some rules.

For example, for this rule, we can decide that we want to automatically change the function name to notDangerous or something else if the fix option of ESLint is true or when we click on the Quick fix button.

We have the fix method in our context.report commented out, uncomment this method, and now click on the Quick fix option should give us more interesting abilities. Also, when we lint, the files get automatically fixed as we have set fix as true.

If we click the button, the function name will change to notDangerous. Let’s lint our files again, but all our errors will be fixed automatically.

And if you check your source code, the dangerous function should have changed to notDangerous.

Next Steps

This article has worked you through setting up and using ESLint with typescript, using the ESLint module rather than the CLI tool, and creating your own custom ESLint rules for your projects.

You can create more rules to enforce styles that you want you and the developers working with you to follow using the idea behind this first rule.

You can check the source code for this article on Github or reach out to me on Twitter.

--

--

I’m a back enddeveloper experienced in building APIs and web applications, I’m also an AI/ML/DL/DS Engineer and a JavaScript enthusiasts, lover of ReactJS.