FrichtiTech
Published in

FrichtiTech

ASTs: How Frichti use them for large & easy refactoring

In this post, we are introducing how to use Abstract syntax trees (AST) to create some eslint custom rules in order to make refactoring much easier and code more suited to your guidelines.

Refactoring

Refactoring is one of the most important parts of the frontend engineer’s job. It is very broad and can go from simple tasks like changing the variable’s name, a function or a component to a complete architecture restructuration, without changing functionalities or introducing regressions.

And as you know… sometimes… refactoring large projects with an historical background and some technical debts could be really painful especially when tasks are just boring like renaming hundreds of variables and sources of imports across hundreds of files.

The problem

Last year, our awesome design’s team released “La Recette”, the first version of the Frichti’s design system. It was a real enhancement for the frontend devs team, as it helps us to improve our development flow and the communication between the product team and developers .

However, the implementation of a new design system in the code base wasn’t as easy as we thought because of many reasons:

  • Colors naming conventions have changed to be more consistent and some colors have totally evolved as you can see the matching table below:
  • We also seized the day of those changes to create a separate npm package in order to reuse our design system through our different frontend apps. That way, the source of import of colors has changed drastically, and we had to convert the whole code base this way:
/** 
** Old legacy code
**/
import { DEPRECATED_COLORS, PRODUCT_CARD_WIDTH } from '@contants/index'
const StyledProductCard = styled.div`
background-color: ${DEPRECATED_COLORS.black};
width: ${PRODUCT_CARD_WIDTH};
`
/**
** New code
**/
import COLORS from '@frichti/la-recette/colors'
import { PRODUCT_CARD_WIDTH } from '@contants/index'
const StyledProductCard = styled.div`
background-color: ${COLORS.dark[900]};
width: ${PRODUCT_CARD_WIDTH};
`

The problem with those challenges was that it was too risky to do a “search and replace” in the whole code base as we could face some cases where the colors were not consistent. Also, developers have to know the matching array and we should inform them for each deprecated color the equivalent one in the new design system. So, we asked ourselves : how can we handle such a tricky subject ?

ASTs to rescue…

“An Abstract Syntax Tree, or AST, is a tree representation of the source code of a computer program that conveys the structure of the source code.” https://deepsource.io/glossary/ast/

To illustrate the AST representation of add function in JavaScript, for example, would look like the following:

https://resources.jointjs.com/demos/javascript-ast

Here we have our Program which is the first node of our tree and then we have a node that defines each action or statement occurring in our code below:

function add(a, b) {
return a + b;
}

Of course, this tree could also be represented as a JSON (JavaScript Object Notation). Concerning our initial problem, for instance:

import { DEPRECATED_COLORS, PRODUCT_CARD_WIDTH } from '@contants/index'

will be represented as the JSON below using the babel-eslint parser:

https://astexplorer.net/#/gist/1ee8aaabdb761bc290664c2ce224e312/fd5f20a95292840c999547deaf1c5b33020d6bdd

Our solution

The solution that we found out for this problem (and many others that I’ll list at the end of this post) was to create a custom eslint rule using ASTs. The idea was to warn the developer who works on a file that uses a deprecated color that he must replace it by the new color and suggest him (or better, auto fix the issue) the equivalent color.

Now, let’s see how we can create our first custom rule!

First step: create the plugin

As we knew that we would create other rules in the future, we decided to create a global eslint plugin to contain all our frontend rules. I’ll not break down here how to create an npm package, there are tons of articles about that, but what you should know is that an eslint plugin should respect a naming convention. The package name should be prefixed by eslint-plugin-*.

In our case we named our package as:

@frichti/eslint-plugin-frontend-rules

Second step: create our rule

Here we will firstly create a rule that will disallow usage of a specific named variable (DEPRECATED_COLORS in our example) and suggest a matching variable (COLORS in this case).

And here we are!! 🎉🎉

https://astexplorer.net/#/gist/1ee8aaabdb761bc290664c2ce224e312/87b52388d975ed6e9f15b1dcbc3fa65d784a3aef

By removing the comments on the fixer, the code will automatically be resolved into this:

https://astexplorer.net/#/gist/1ee8aaabdb761bc290664c2ce224e312/e27af3bbee4a3c05fd471beffdfc7a9c4d2520b4

Conclusion

Our rule is more configurable and offers some other options like matching object properties with new ones, as we could replace the DEPRECATED_COLORS.black by the new COLORS.dark[900] for instance.

Finally, our package is available on npm and the whole code can be found on Github. Feel free to install the npm package or contribute with new pull request.

Of course, this example is kept simple in order to understand some of ASTs pros and what it offers. A lot of other things can be done using them like babel plugins, codemods….

At Frichti, we internally created a lot of other eslint rules to remain consistent in our code base, reinforce some guidelines rules or make some long refactoring tasks easier to develop. For example, we have:

  • A rule that disallows calling analytics methods more than once in another method.
  • A rule that disallows direct xState context assignment without passing by assign (the @xstate/immer one or native one) method.
  • And a lot of other specific cases rules

Thanks for reading, feel free to comment or reach out for questions !

Cheers 🍻

--

--

Stories from Frichti's Tech team.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store