Table of Contents
Codemods are widely used in front-end development with JavaScript to help with codebase maintenance.
As the JavaScript language evolves, new features are added that can make existing code more efficient. To take advantage of these improvements, codemods can automate the process of updating large code bases.
Why Codemod?
Refactoring is an integral part of code maintenance and codemods can help to simplify this process through automation. This reduces the time spent by developers and makes refactoring more efficient.
When a codebase needs to make a large-scale change, such as updating a library on which it relies, codemods can assist in automating the process and reducing the risk of bugs or other issues.
Codemods contribute to code standardisation by modifying existing code to adhere to a style manual or best practices established within teams.
Elements of Codemod
A codemod includes the following elements:
- Input code – the code that the codemod will transform
- Transformation – a set of Instructions for transforming input code. Transformations can range from simple string replacements to complex code generation.
- Output code – Transformed code produced by applying the transformation
Abstract Syntax Tree (AST)
An AST is a tree representation of the abstract syntax of source code. Each node of the tree represents a construct in the source code and the node provides important information about the construct. A browser-based tool called ASTExplorer can help you understand and parse your code’s tree.
AST for the above code:
The three main object types that the JSCodeshift API works with are: nodes, node-paths, and collections.
Nodes
Nodes are the basic building blocks of the AST, often referred to as “AST nodes.” These are what you see when exploring your code with AST Explorer. They are simple objects and do not provide any methods.
Node-paths
As a means of traversing the abstract syntax tree (AST), node-paths are wrappers around an ast-type node. Node-paths take care of this because, in isolation, nodes do not have any information about their parent or scope. We can access the wrapped node through the node property, and the underlying node can be modified using various methods. The term “paths” is frequently used to refer to node-paths.
Collections
A group of zero or more node-paths that the JSCodeshift API returns when you query the AST. They have all sorts of useful methods, some of which we will explore.
“Collections contain node-paths, node-paths contain nodes, and nodes are what the AST is made of”, Remembering this will be easy to understand the JSCodeshift query API.
Dependencies for Codemod
JSCodeShift
JSCodeshift is a toolkit for running a codemod over multiple JavaScript or TypeScript files. It provides an Abstract Syntax Tree (AST) representation of the input code, making it easy to write the transformation instructions. A testing framework is also available to help ensure that the transformation is correct and doesn’t introduce bugs.
Recast
An essential component of JSCodeShift that provides parsing and code generation capabilities. Recast takes JavaScript code as input and produces an AST. The generated AST can be manipulated and then a JavaScript code is generated as output with the help of recast.
Some Examples
We’ll look at a few scenarios where codemods were used at Cashfree Payments:
- Changing a package name in all the files.
- Refactoring a dependent component.
Let’s set up codemod in our repository with the following steps:
- Install jscodeshift
- In our package.json, assuming src to be a parent folder, add a script to run your codemod “codemod”: “jscodeshift ./src –extensions=js,jsx,ts,tsx -t transform.js -p”
- In transform.js, add the following code
Example 1: Changing a package name in all the files
In our activity, we are trying to explore the AST for:
Head over to AST-Explorer to find the abstract syntax tree for your code. There is a lot of data even in the simplest code, so it helps to hide location data and methods. You can toggle the visibility of properties in AST Explorer with the checkboxes above the tree.
We can see that import statements are referred to as Import Declaration.
The code above returns a collection of one node-path, which wraps the root AST node. We can use the collection’s find method to search for descendant nodes of a certain type in our case ImportDeclaration in such a way:
This returns a filtered collection of node-paths containing just the nodes of type ImportDeclaration.
We see in AST Explorer that the import statement from ‘react-library’ has the form of:
So, re-defining our query to find more specific imports
Now that we have all the imports from package ‘react-library’ changing to ‘@internal/react-library’, we will have to change for each node-paths source value as follows:
Now, on npm run codemod, you will notice that all the javascript files we had imported from ‘react-library’ must have changed to ‘@internal/react-library’.
Example 2: Refactoring a dependent component
Let’s try importing a NewButton from ‘new pkg’ instead of ButtonX from ‘oldpkg’, also reflecting the same in SomeComponent and getting rid of the primary prop.
Again, let’s head to AST explorer.
Import is of the type ImportDeclaration, so we just filter the imports with a specific check for the source of import having the value ‘oldpkg’. ButtonX is of the type ImportSpecifier, using that, we change the node imported name from ButtonX to NewButton
Also, ButtonX is a type of JSXElement where the opening and closing tags are of the type JSXOpeningElement and JSXClosingElement respectively, and the primary prop is a type of JSXIdentifier. So we just need to query for those types respectively and change the node-paths name.
On running codemod, we would see the following output:
Codemod at Cashfree Payments
At Cashfree Payments, codemod has helped us immensely during code refactoring and large-scale migrations. Also, whenever a component has breaking changes in our internal library, a codemod is recommended so that other developers can easily make the changes in other projects. This helps save time and reduce the likelihood of other developers making mistakes during the change.
In Conclusion
Codemods are superior to the find and replace function because, unlike codemods, find and replace allows for human error and requires us to manually search for each necessary change one after the other.
Codemods, on the other hand, are smart enough only to replace relevant parts of your source code, patching in changes while leaving the rest of the file intact. They also have the additional benefit of being maintainable over manual refactoring.
Writing codemods may feel a little time-consuming but with a little understanding of AST and practice, you will get further intrigued to write codemods.
