Logo

dev-resources.site

for different kinds of informations.

ESLint Plugin. What was missed in the doc?

Published at
5/9/2024
Categories
eslint
tutorial
ast
Author
xavescor
Categories
3 categories in total
eslint
open
tutorial
open
ast
open
Author
8 person written this
xavescor
open
ESLint Plugin. What was missed in the doc?

ESLint is a potent tool, but you cannot create a good plugin because the documentation doesn't explain how it works. For example, you want to do something with non-closured variables inside a function. It is not a trivial task. The first idea that came to my head was to find similar solutions in open source. The most popular is the react-hooks/exhaustive-deps rule. But if you try to see the sources you meet a total horror. Let's try to understand how to avoid this complexity inside ESLint plugins.

Prelude

Let's open the official tutorial on how to create a plugin. It looks nice, and I suppose the authors of the React plugin saw this tutorial. Why am I thinking like that? Because they use a single attach to an ESLint traverser(LOL. line 1358)

It is a bad idea because the plugin tries to traverse the AST tree by itself. Let's try to use the ESLint traverse instead of creating our own.

This move allows you to simplify our code significantly. Why can we do what?

How does ESLint work?

The central meaning of the article is ESLint use DFS to traverse the AST tree. Why is it important?

Let's see a plugin structure:

create(context) {
  return {'Selector': callback}
}
Enter fullscreen mode Exit fullscreen mode

No. It is a bad view of the plugin structure. Let's add the meaningful markup to the code.

create(context) {
  // Memory
  return {'CSS-like selector': callback}
}
Enter fullscreen mode Exit fullscreen mode

Let's remember this and look up to another place.

Babel and async code

Do you know how Babel transforms your async code for Internet Explorer? Link to babel playgroud

The source code was simple

async function foo() {
  await 1;
}
Enter fullscreen mode Exit fullscreen mode

Yes, Babel transforms our async code to the state-machine. And... structure is similar to our plugin structure. We have cases(CSS-Selectors) and a memory part.

Pseudo-async version of plugins

In a better world, the plugin would look like this:

async create(context) {
  // do smth
  await waitForSelector('Selector');
  // do smth
  await waitForSelector('Selector 2');
}
Enter fullscreen mode Exit fullscreen mode

And we can transform this code to babel-like output:

create(context) {
  let flag1 = false;

  return {
    'Selector': () => {flag1 = true},
    'Selector:exit': () => {flag1 = false},
    'Selector2': () => {if (!flag1) {return} /* do logic */}
  }
}
Enter fullscreen mode Exit fullscreen mode

You can use memory to save and toggle flags inside state-machine logic. Do you remember about BFS? Yes. We can use selectors to set am I inside block flags to determine whether we should execute logic.

And I use it inside my plugin:
Memory

Traverse
Image description

How to use this info?

Let's try to implement the logic from react-hooks/exhaustive-deps: we need to run the analysis inside the defined before functions. For instance, inside useCallback.
You can write like the React team:

create(context) {
  return {
    `CallExpression`: traverseAndAnalyse,
  };
}
Enter fullscreen mode Exit fullscreen mode

But you can remember about ESLint traverse logic and memory place:

const functionNameFromConfig = 'useCallback';

create(context) {
  let insideF = false;
  function enterF() {insideF = true;}
  function exitF() {insideF = false;}
  function runAnalysis() {
    if (!isnideF) {
      return;
    }
    // analyse
  }

  return {
    `CallExpression[callee.name="${functionNameFromConfig}"]`: enterF,
    `CallExpression[callee.name="${functionNameFromConfig}"]:exit`: exitF,
    `AnyExpression inside function`: runAnalysis,
  };
}
Enter fullscreen mode Exit fullscreen mode

Think async, but write code like Babel. Do not traverse AST by yourself. It is a significant simplification of the code.

Useful links

https://astexplorer.net - a helpful tool to see the AST version of your code
ESLint Selectors - the list of selectors
typescript parser playground. astexplorer analogue for typescript. It has an essential feature: ESQuery filter. It allows you to debug your selectors in real-time.

ast Article's
30 articles in total
Favicon
Understanding Abstract Syntax Trees
Favicon
ESLint Plugin. What was missed in the doc?
Favicon
Code search and refactoring tools - `Code Recycle`
Favicon
Code cycle: may be the syntax query that currently supports the most languages
Favicon
Python, ast, and redbaron
Favicon
Generate references table from code comments
Favicon
Effective Refactoring with Codemods
Favicon
TagTide library: make your HTML editor-friendly and more
Favicon
What is AST?
Favicon
Analyzing AST in Go with JSON tools
Favicon
Deciphering Python: How to use Abstract Syntax Trees (AST) to understand code
Favicon
Revealing the magic of AST by writing babel plugins
Favicon
From Parse Tree to Evaluator (featuring Sarah Withee)
Favicon
Digging deeper into the analysis of Go-code
Favicon
Take a walk the Go AST
Favicon
OpenTracing for Go Projects
Favicon
My journey optimizing the Go Compiler
Favicon
Static Code Analysis: What it is? How to use it?
Favicon
How to find what is the dependency of a function, class, or variable in ES6 via AST
Favicon
JS-X-Ray 1.0
Favicon
How to work happier with QA with the help of AST
Favicon
AST Finder – Finding AST nodes from code
Favicon
Building AST nodes from source code
Favicon
Adding Contexts via Go AST (Code Instrumentation)
Favicon
Manipulating AST with JavaScript
Favicon
Easier TypeScript tooling with TSQuery
Favicon
Converting TypeScript decorators into static code!
Favicon
Babel macros
Favicon
How do template literals in JavaScript work under the hood?
Favicon
Creating custom JavaScript syntax with Babel

Featured ones: