How to Create Codemods Using jscodeshift
Using jscodeshift to make the same code transformations with less boilerplate
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
This lesson preview is part of the Practical Abstract Syntax Trees course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Practical Abstract Syntax Trees, plus 70+ \newline books, guides and courses with the \newline Pro subscription.
![Thumbnail for the \newline course Practical Abstract Syntax Trees](/_next/image?url=https%3A%2F%2Fs3.amazonaws.com%2Fassets.fullstack.io%2Fn%2F20211005114103016_CoverImage_PracticalAbstractSyntaxTrees_v2.png&w=1920&q=75)
[00:00 - 00:12] In order to create a code mod, we needed to install several dependencies in boilerplate code for reading, parsing, and writing files. This is where a more specialized tool like JS code shift becomes useful.
[00:13 - 00:21] It's a toolkit for running code mods. It takes care of all the dependencies in boilerplate for finding, reading, parsing, and writing back to files.
[00:22 - 00:45] It also has some other nice features for working in large code bases, like running a transform in parallel on multiple files, as well as a summary of how many files were changed. Let's again get started by switching to our terminal and creating a new directory called JS code shift, and initialize another package.json.
[00:46 - 00:57] This time, the only dependency we need is JS code shift, but we'll also install prettier for consistent formatting. We can also install the types for both.
[00:58 - 01:07] Again, the only required dependency here was JS code shift. Critier was installed, again, to consistently format code, but that's a nice to have.
[01:08 - 01:16] The types were also installed since we're using TypeScript. Then create a new transform.ts file.
[01:17 - 01:30] We can then add the necessary boilerplate for a JS code shift transform. We'll start by importing the transform type from JS code shift, because it defines the call signature for the transform function.
[01:31 - 01:54] Then we can define the transform function, which takes in a file info object, as well as an API object. For convenience, we'll alias the JS code shift API to the variable J. Then, using this alias J, we'll invoke it with the source from the file info object, which will give us the root of the AST.
[01:55 - 02:04] And finally, we'll return the root converted back to source. And lastly, we'll make this transform function the default export.
[02:05 - 02:17] This transform function follows the basic JS code shift API, which is a function that is the default export that accepts three arguments. It also supports transforms written in TypeScript with no additional setup.
[02:18 - 02:36] Notice that we didn't have to install anything except J is code shift in its types. Now this basic transform can be executed using JS code shift, with the path to the files that we want to transform, as well as the transform file to the transform flag.
[02:37 - 02:49] There are several other options that JS code shift accepts. For example, if working with a code base written in TypeScript, it would require overriding the extensions flag option to TS or TSX, since it defaults to JS.
[02:50 - 02:57] Additionally, it would require passing TS or TSX for the parser option. The default JS code shift parser is Babel.
[02:58 - 03:06] The other options are Babylon, Flow, TS, or TSX. This can be somewhat confusing because Babylon was the previous name of Babel parser.
[03:07 - 03:20] The default Babel parser is a legacy configuration option to behave like Babel version 5, while Babylon is a more modern configuration. Additionally, the Babel parser adds the ES tree Babel plugin.
[03:21 - 03:37] It changes the types of nodes in the AST, which can be confusing if using the Babel parser plugin option in the AST Explorer. The ES tree plugin merges different types of nodes like string literal and numeric literal into a single literal node, along with several other changes.
[03:38 - 03:52] While it isn't strictly necessary in this case, we will still pass Babel on as the parser option to avoid some of this confusion and use the more modern configuration. Right now, this transform isn't doing anything, but we have verified that it's all working properly.
[03:53 - 04:09] The setup has taken care of everything except the custom logic of transforming button elements into button components. JS code shift has taken care of all the dependencies, finding relevant files, reading files, parsing files into an AST, and writing that AST back to files.
[04:10 - 04:25] Additionally, it provides convenient helpers like find JSX elements when specifically transforming React code. Lastly, it runs the transformations in parallel, and outputs a summary of the result, both of which become more important as the size of the code base grows.
[04:26 - 04:34] The output shows it found 14 files that matched that provided path. It then processed those files in various workers and printed the results.
[04:35 - 04:46] Since the transform isn't doing anything yet, all 14 files were unmodified. Much of the same logic from the previous lesson can be reused for traversing the AST and making the necessary mutations.
[04:47 - 04:55] Since we're now using JS code shift helpers, it will look a little different. Let's switch back to our editor and add a few imports that we'll need.
[04:56 - 05:06] First, the JSX attribute type from JS code shift. We'll also need nodes path utilities as well as prettier.
[05:07 - 05:16] Then we'll still want to keep track if the file contains any buttons. For now, let's skip over the traversal logic.
[05:17 - 05:27] If the file contains any buttons, then we need to add the import as before, as well as apply the prettier format. If the file doesn't contain any buttons, then we don't need to make any changes .
[05:28 - 05:40] In this case with JS code shift, we can return null, which signifies that no change is resulted. Let's switch to the previous transform and copy this related logic.
[05:41 - 05:51] And we no longer need this generate call. Instead, we can pass the root source to prettier.
[05:52 - 06:06] And the file path is on the file info object. Additionally, we no longer have the types package, but we do have the JS code shift API aliased with J, which also has all of the same builder functions.
[06:07 - 06:14] And we no longer directly have access to the AST. However, we do from the root node.
[06:15 - 06:24] Now we can add the traversal logic. We can use JS code shifts to find JSX elements helper method to find all of the button elements.
[06:25 - 06:43] This will return an array of button elements. We then want to filter these using the same filtering logic from before.
[06:44 - 07:00] We can copy this logic from the previous transform. And now with the remaining nodes, we want to replace these nodes with the new updated button component in button prop nodes.
[07:01 - 07:15] We can again use a JS code shift replace with API, which passes in the node and we can return a new node to replace it with. First, once we're here, we know that this file contains a button element that will be transformed, so we can update the boolean.
[07:16 - 07:32] Same as before, we can create a new props array that represents the new props for the button component. We can then copy all of the logic for looping through the props and transforming them from the previous script.
[07:33 - 08:03] Again, the Babel builders can be replaced with the JS code shift builders. Now, we can create a new JSX opening element to replace the existing nodes element as well as update the closing element and return the mutated node.
[08:04 - 08:19] The script should now be identical in functionality to the custom script. Once you're the previous lesson, this script finds all of the JSX element nodes that represent a button element using the JS code shift, find JSX elements helper on the root of the AST.
[08:20 - 08:32] Then, it filters out button element nodes that don't match our criteria. Next, it transforms the existing props into the button component props and uses the JS code shift to replace with helper to replace the existing node.
[08:33 - 08:40] And finally, it creates an import statement and inserts it at the top of the file. The completed script can be run again.
[08:41 - 08:56] Remember to revert any changes from the previous lesson in the sample code base before running if you haven't. This time, the results look a little different.
[08:57 - 09:07] It still found 14 files, but four were okay and 10 were skipped. The four files marked okay were those that had button elements transformed into button components successfully.
[09:08 - 09:19] The other 10 files didn't contain any transformations. These are marked as skipped instead of unmodified this time because we returned null instead of converting the root back to source if there were no transformations.
[09:20 - 09:34] All button elements should now be button components with the props transformed in the flashcards code base. When performing higher level transformations such as renaming variables or components, reaching for JS code shift can help streamline a lot of the boilerplate.
[09:35 - 09:49] However, the documentation isn't robust and the helpers are only visible in the source and the AST logic needs to be updated to the JS code shift specific API. Creating either a custom transform or a JS code shift transform can both be viable options.
[09:50 - 10:01] In practice, JS code shift can handle most transforms and is a good place to start. Another benefit of using JS code shift is the built-in utilities for testing to transform, which will be covered in the next lesson.