ESLint Understand By Doing Part 1: Abstract Syntax Trees
Tags: javascript, tutorial, productivity, testing
Published: 2023-03-30 02:39:34

Like everything in software development, its best to learn by doing. In this blog series we will go over what is happening, under the hood, when you lint your JavaScript files by writing our very own custom eslint rule!

## Setting the stage
Like many I am always searching for answers to life's greatest questions. Also like many, I can narrow these questions down to the three most elusive:

1. What happens when we die?
2. Do aliens exist?
3. How does ESLint work?

Due to recent advances in modern sciences we can finally begin to understand the most important of these questions: How does ESLint _work_?

## TL:DR
I have no idea what I am talking about.

## Abstract Syntax Trees
When you run ESLint, the first thing it does is create an Abstract Syntax Tree (AST) of your code. Basically an AST is a description of your codes syntax. A tool can use this description to evaluate your codes structure.

ESLint is not the only tool that uses ASTs. The TypeScript parser creates an AST in its own format to check for incomplete code, type checking, and if you have used the `any` type implicitly (hehe).

For this blog we will focus on how ESLint uses an AST to understand your code.

## Learn by doing
The best way to understand what an AST is.. is to simply play with them over on [AST Explorer](https://astexplorer.net/). I like to switch the right side panel to `json` as I find it easier to follow, but you do you.

You can follow along with this example:
```javascript
// Simply setting a variable `foo` to a string 'bar'
const foo = 'bar';
```
ESLint's AST format, [ESTree](https://github.com/ESTree/ESTree), would represent this line of code as:
```json
{
  "type": "Program",
  "start": 0,
  "end": 16,
  "body": [
    {
      "type": "VariableDeclaration",
      "start": 0,
      "end": 16,
      "declarations": [
        {
          "type": "VariableDeclarator",
          "start": 6,
          "end": 15,
          "id": {
            "type": "Identifier",
            "start": 6,
            "end": 9,
            "name": "foo"
          },
          "init": {
            "type": "Identifier",
            "start": 12,
            "end": 15,
            "name": "bar"
          }
        }
      ],
      "kind": "const"
    }
  ],
  "sourceType": "module"
}
```
You can gather a lot of information from this. To help we can break it down, then build it back up while we describe whats going on.
### The Root

```json
{
  "type": "Program",
  "start": 0,
  "end": 16,
  "body": [
    // Not important right now
  ],
  "sourceType": "module"
}
```
The root of this object has a `type` of 'Program'. This is standard and encapsulates the the rest of the _program_ code. It shows what byte the program code starts and ends, in the file, as well as describes the type of structure your file has, like whether it is a commonJS file or a ES module file.

### The Body
```json
{
  "type": "Program",
  "start": 0,
  "end": 16,
  "body": [
    {
      "type": "VariableDeclaration",
      "start": 0,
      "end": 16,
      "declarations": [
        // Not important right now
      ],
      "kind": "const"
    }
  ],
  "sourceType": "module"
}
```
In the `body` of the program you have an array of objects. This is where everything you write lives. Since we are only declaring a variable in our program, there is only one object whos type is of `"VariableDeclaration"`. If we were to add a function to the file, a new object would pop up in the `body` array (specifically it would be a `"FunctionDeclaration"` object).

Looking back at the `"VariableDeclaration"` object you will notice it has the familiar `start` and `end`, as well the `kind` of variable it is (its a constant).

### The Meat
```json
{
  "type": "Program",
  "start": 0,
  "end": 16,
  "body": [
    {
      "type": "VariableDeclaration",
      "start": 0,
      "end": 16,
      "declarations": [
        {
          "type": "VariableDeclarator",
          "start": 6,
          "end": 15,
          "id": {
            "type": "Identifier",
            "start": 6,
            "end": 9,
            "name": "foo"
          },
          "init": {
            "type": "Identifier",
            "start": 12,
            "end": 15,
            "name": "bar"
          }
        }
      ],
      "kind": "const"
    }
  ],
  "sourceType": "module"
}
```
Now to bring it all back by adding the meat, the `"declarations"` property. Inside of this property is where the meat is. The `id` object is where the _name_ of our variable is housed. Likewise, the `init` property is where the _value_ our variable was initialized too is house. Each of these nested objects also has a `type` property and their own `start` and `end` properties. Can you see a pattern?

That's all an AST is. It simply describes what and where the individual pieces of your code are. This is the map ESLint uses to run its rules off of.

In part 2 of this series we will show how ESLint does this by creating rules of our own.