React & Codemod Announcement
We're excited to announce that we are partnering with the React team to create an exceptional migration experience for their recent and upcoming releases. Read on to learn ...
Partnerships, customer stories, technical deep dives, and more.
We're excited to announce that we are partnering with the React team to create an exceptional migration experience for their recent and upcoming releases. Read on to learn ...
AI-assisted codemod creation is now available locally in any IDE via Codemod MCP, which takes a different approach using conventional MCP patterns with tools for building JSSG codemods. We are thrilled to announce a new feature in Codemod AI that empowers users to provide transformation logic in the form of natural language descriptions directly within the code examples. This improvement builds on top of our existing iterative approach which significantly improves the accuracy of AI-generated codemods. By leveraging this additional context, Codemod AI can generate more precise and accurate codemods and further reduce the burden on developers. Example Let’s walk through an example use case to see how you can benefit from this new feature. Take the following before and after code examples for which we want an AI model to generate a codemod. Before Example: const mapStateToProps = (state) = ( a: selectA(state), ); After Example: import State from "state"; function mapStateToProps(state: State) const data = state; return data ; The codemod which is generated for this pair of code snippets is not generalizable to functions other than mapStateToProps . Here is where the new feature comes in. The user can add some descriptions to the “After” snippet (like below) to get a more generalized codemod. After Example with Additional User Description: import State from "state"; // the codemod should work on any function argument which is named "state", and not just for "mapStateToProps" function function mapStateToProps(state: State) const data = state; return data ; This time, Codemod AI generates a generalizable codemod that is applicable to any function that has an argument named state . Get Started Today We invite you to explore this new feature and see how natural language descriptions can transform your codemod generation experience. Your feedback is invaluable as we continue to refine and expand our tool to better serve the community.
Codemod is the go-to platform for its users as well as open-source framework builders who are looking forward to migrate their next project.
Since this post was published, JSSG has become the primary transformation engine for the Codemod platform. JSSG is a more powerful alternative built on ast-grep that handles both JavaScript and TypeScript. At Codemod, our mission is to create an exceptional code migration experience for developers worldwide. To meet all code migration needs, we've designed our system to work with various popular, proven, and open-source codemod engines, each with its own strengths and weaknesses. We are excited to announce that we've added support for the ts-morph engine to Codemod AI, building on the foundation laid out in our previous blog post, where we introduced our iterative approach to codemod generation. ts-morph is a powerful TypeScript manipulation library for programmatic code changes. This expansion empowers developers to tackle codebase modernization challenges using ts-morph alongside jscodeshift. Our experiments, using our publicly available registry as a test set, show promising results. With vanilla LLM (zero refinement iterations), the accuracy of codemod generation given the before and after code examples is only 26%. However, with just a few refinement iterations, we've seen significant improvements. For example, after four iterations, the accuracy improves to 54%. We used GPT-4o in our experiments. For context, even the initial 26% success rate, achieved without our iterative AI system, used expert-curated prompts, enhancing the developer experience compared to using ChatGPT/GPT-4o directly. We hope this update extends our reach to the TypeScript community. While our refinement iterations for ts-morph are currently basic and lack RAG or any advanced optimizations, we are actively working to enhance this process to provide more accurate and efficient solutions. Join us at Codemod on this journey of innovation with Codemod AI. Your feedback is invaluable as we strive to deliver cutting-edge solutions for code modernization and migration challenges.
Learn how Codemod AI blends software analysis, a knowledge base, and a feedback loop to outperform standard LLMs like GPT-4o in generating codemods.
When you start a greenfield project, AI allows you to ship at the speed of thought. However, as your project grows in size, your code may not evolve as quickly as you would like. It doesn't have to be this way. Andrew and I would love to refer to such code as "elegant," which has a straightforward definition. The term "codemod" refers to a tool that assists developers in making systematic changes to their source code. A codebase becomes more "codemodable" when it is structured in a way that allows for these automated modifications to be performed easily and reliably. With that, fleet-wide modernization becomes possible, and that’s crucial especially as the size of codebases grows exponentially thanks to AI. Here are some factors that can make code more or less amenable to codemodding: Consistency If the code follows consistent patterns and styles, it's easier to apply a codemod because the tool can predictably identify the parts that need changes. More codemodable : Functions and methods follow a consistent naming convention, making it easier to target them. function calculateTotal() / ... / function calculateDiscount() / ... / Less codemodable : Inconsistent naming makes it difficult to identify related functions. function calcTotal() / ... / function getDiscountAmount() / ... / Complexity Simpler, less complex code with fewer dependencies is typically easier to modify with a codemod. More codemodable : Simple, single-purpose functions. def add(a, b): return a + b Less codemodable : Nested functions with complex logic. def calculate(a, b, option): if option == 'add': def add(x, y): return x + y return add(a, b) More complex logic... Test Coverage Having a comprehensive suite of tests allows developers to ensure that the codemod has not broken any existing functionality. More codemodable : Code accompanied by tests. function add(a, b) return a + b; console.assert(add(2, 2) === 4); Less codemodable : Code with no tests. function add(a, b) return a + b; Modularity Code that is modular and composed of small, single-responsibility functions or classes is easier to target with codemods because changes can be isolated to specific modules without affecting others. More codemodable : Code organized in modules or classes. class Calculator add(a: number, b: number) return a + b; Less codemodable : All logic in a single file or function. function calculate(a: number, b: number, op: string) if (op === "add") return a + b; // More operations... API Stability If the code relies on APIs that are stable and backward compatible, codemods can be written with the confidence that the API contract won't change unexpectedly. More codemodable : Using well-established libraries or APIs. import requests response = requests.get(' ') Less codemodable: Using custom, frequently changing APIs. import myCustomApi response = myCustomApi.get(' ') Abstraction Level Code that operates at a higher level of abstraction may be easier to codemod because changes can be made at the abstraction level rather than having to account for many low-level details. More codemodable : High-level abstractions. public interface IShape void Draw(); Less codemodable : Low-level, detailed implementations. public class Circle // Complex drawing logic involving graphics primitives Making code more "codemodable" often involves cleaning up and refactoring the codebase to improve these aspects, which not only facilitates the use of codemods but generally leads to a cleaner, more maintainable, and more robust codebase.
This migration was built using jscodeshift. JSSG is now the primary transformation engine for the Codemod platform, offering a more powerful alternative built on ast-grep. Introduction JavaScript toolchain has been stable for around 10 years now, with tools like ESLint and Prettier , it might have seemed like regular commit-to-commit tasks, such as code linting & formatting have been solved, and there is not much need to improve on top of existing solutions. However, what if there was a tool that is much easier to configure, holds a configuration for both linting and formatting in the same place, and runs DOZENS (!) of times faster? Well, that’s what Biome.js was built for! While the curiosity to try out the solution is already there all over the social media and some companies are actively trying it out for new projects, the hard part is if you want to migrate your existing configuration with hundreds of rules working nicely together to be migrated to Biome. Although there might not be a replacement for every single rule in ESLint , there are certainly replacements for the most popular and used ones, including React JSX and even some a11y rules! Well, the good news is that trying out Biome, while preserving what’s possible from your existing configuration, has never been easier. The Codemod platform allows you to migrate your existing set of ESLint and Prettier configurations to Biome without much hassle. Get your set of rules to migrate by running a simple ESLint CLI command and feeding it into our codemod. We will do the rest for you. Understanding the Migration Landscape Designed to be fast, optimized, scalable, and simple, Biome is very easy to get started with. It’s 97% compatible with code formatting that you all are used to with Prettier and requires less configuration to make it work similarly. It has a lot of essential rules that you would naturally use in your ESLint configuration, but they are also much easier to set up, run much faster, and have descriptive errors that tell precisely where the error is located and how to fix it. For most of the cases, it’s hard to migrate off of ESLint and Prettier because of the huge ecosystem they have built around them. But, the simplicity and speed behind it have their benefits. It’s 25x times faster than Prettier and 15x times faster than ESLint . It has most of the essential rules available right away, it’s incredibly easy to set up and get running, it has first-class support for TypeScript and JSX, and even has an extension or a way to set up in any editor of your choice. It even has organize imports rule section to replace similar plugin in Prettier and has some of the most requested features in development that will soon be released, such as sorting tailwind classes and many others. All that said, it’s still a lot of manual work to migrate all the config files to a single biome.json and make it work. This might take a day for a relatively simple codebase, just as it could take a week for a giant monorepo with dozens of apps and packages that all need to share the same code formatting rules. This is where codemods become handy. With the help of our Codemod platform, you will be able to switch in no-time and try it out quickly to make a decision! The Codemod Platform Our Codemod platform assisted in building the mentioned codemod and running it across our codebase. First of all, we had to think about what needs to be done in the course of the migration. We have to read all ESLint and Prettier related files, and based on what’s inside of them, build a biome.json configuration file. We should replace all the ESLint and Prettier commands in package.json files’ script field to use biome We have to also update the dependencies in package.json files where ESLint is installed. A known shortcoming of existing transformation engines, like jscodeshift or ts-morph is the fact that they cannot really create/delete/move files. They also can’t make updates to other files based on previously parsed file. This is where our filemod engine comes into play. When parsing a file, it allows us to execute multiple actions in different locations of the filesystem. For example, we could parse .eslintrc file and based on it’s contents update (upsert, in fact) biome.json while at the same time deleting the original .eslintrc as it is not needed anymore. This way we have introduced a solution that allows to make drastic coupled changes in different parts of the codebase, while maintaining the initial parsing order. Security is a top priority in the Codemod platform. That’s why we have limited codemods to a certain extent to prevent codemod creators from abusing the possibilities that Node.js platform provides in terms of access to the user system. We only pass certain functions and features down the pipeline to be accessible within a particular codemod. In the mentioned migration, we had to fetch a raw markdown file which contains mappings from ESLint to Biome rules and fetch Biome website pages to determine how to activate this rules within biome.json . So, we only passed fetch function down the line and made use of this particular Node.js feature. We don’t do any extra. We named the codemod for the described migration eslint/biome/migrate-rules . Running the codemod was fun. It only required us to find a file that ESLint performs checks on using the most rules. In the next section we’ll talk about the experience. Migrating Codemod.com’s Own Codebase & Results Introduce the Codemod Monorepo codebase as the subject of your case study. So, we wanted to try this tooling for ourselves. We decided to test the recipe over our monorepo. We wrapped our sleeves, found a file that supposedly gets linted by the most amount of rules in our ESLint config, and ran: npx eslint --print-config apps/vsce/webview/src/main/index.tsx npx codemod eslint/biome/migrate-rules In this command: npx eslint --print-config apps/vsce/webview/src/main/index.tsx - to print the config used for a particular file, unwrapping ALL the rules that hide under configs that our own configuration extends from. In this example we used .tsx file to include possible React rules. npx codemod eslint/biome/migrate-rules - feed the input of previous command to our CLI. As shown in the table below, our migration to Biome was well worth it. Time (ESLint + Prettier) Time (Biome) 15.6 seconds 1.6 seconds Biome was noticeably faster and easier in use, so we decided to stick with it for the foreseeable future. It was a matter of getting rid of all the ESLint configuration files and installed dependencies, including compat plugins for Prettier to work nicely with it. After the migration, we didn’t notice that we lost some of the rules. It was working as expected perfectly from the get-go. If we were to manually migrate the Codemod Monorepo alone, we would have to: adjust 134 existing package.json files: replace ESLint scripts (1 minute per file); replace ESLint deps (1 minute per file); find a corresponding rule for each of the existing ESLint rules (2 minutes per rule); replace Prettier configuration (5 minutes) It would have taken up to a whole day of boring mechanical work to perform the migration manually without performing any additional troubleshooting. Our platform made it all happen by running a single command.
Why bother migrating to Vitest? Vitest is a modern test runner with improved perfomance and developer experience comapred to many other existing alternatives, which explains its growing popularity. More specifically, Vitest simplifies configuration, supports TypeScript & ESM, and popular component libraries out of the box. Last but not least, it comes with code coverage analysis, and overall, saving hours of tedious work for developers. But, the manual migration from Mocha.js to Vitest could take a considerable amount of time, especially when you have a lot of test suites. What if there were code modification bots that could automate the entire migration process? Codemod: The code automation platform Building codemods requires knowledge of abstract syntax trees and compiler internals. The Codemod platform simplifies this by providing tools to build, test, share, and run codemods at any scale: Build : Use Codemod Studio to create and test codemods visually, powered by JSSG (our primary transformation engine built on ast-grep). Share : Publish codemods to the Codemod Registry , making them available to colleagues or the community. Run : Discover and run codemods with a single CLI command. Orchestrate : Use Codemod Workflows to chain multi-step migrations across repositories. Mocha to Vitest migration with Codemod.com Build the codemod To build a codemod with Codemod Studio, you provide code snippets showing the before and after states of the transformation. The Studio uses AI to generate the codemod logic, which you can then refine and test interactively. Imagine we have an existing Mocha.js-compatible test file: Imagine we have an existing Mocha.js-compatible test file with the following code, which we place in the “before” snippet: import type Context from 'mocha'; import expect from 'chai'; describe('Test Suite 1', function (this) beforeEach(() = doAThing(); ); it('addition', () = expect(1 + 1).to.equal(2); ); afterAll(() = doBThing(); ); ); With Vitest, we would write the test file in the following way, which we place in the “after” snippet: import describe, it, expect, beforeEach, afterAll from 'vitest'; describe('Test Suite 1', () = beforeEach(() = doAThing(); ); it('addition', () = expect(1 + 1).to.equal(2); ); afterAll(() = doBThing(); ); ); As you can see, in Mocha.js describe , beforeEach , it and afterAll functions exist in the global scope. Additionally, we chose to use the expect function from the chai library explicitly for test assertions. In Vitest, we need to import all of these functions from the vitest package. import type FileInfo, API, Options from 'jscodeshift'; export default function transform( file: FileInfo, api: API, options?: Options, ): string undefined const j = api.jscodeshift; const root = j(file.source); // Helper function to preserve leading comments function replaceWithComments(path, newNode) // If the original node had comments, add them to the new node if (path.node.comments) newNode.comments = path.node.comments; // Replace the node j(path).replaceWith(newNode); // Check if beforeEach and afterAll are used in the code const isBeforeEachUsed = root.find(j.Identifier, name: 'beforeEach' ).size() 0; const isAfterAllUsed = root.find(j.Identifier, name: 'afterAll' ).size() 0; // Find all import declarations root.find(j.ImportDeclaration).forEach(path = // Check if import source is 'chai' if (path.node.source.value === 'chai') // Replace 'chai' with 'vitest' path.node.source.value = 'vitest'; // Add 'describe', 'it', 'expect', 'beforeEach', 'afterAll' to import specifiers conditionally const importNames = ['describe', 'it', 'expect']; if (isBeforeEachUsed) importNames.push('beforeEach'); if (isAfterAllUsed) importNames.push('afterAll'); path.node.specifiers = importNames.map(name = j.importSpecifier.from( imported: j.identifier(name), local: j.identifier(name) ) ); // Replace the node with the new node, preserving comments replaceWithComments(path, path.node); ); // Find all function declarations root.find(j.FunctionExpression).forEach(path = // Check if function has 'this' as parameter if (path.node.params.some(param = param.type === 'Identifier' && param.name === 'this')) // Remove 'this' from parameters path.node.params = path.node.params.filter(param = !(param.type === 'Identifier' && param.name === 'this')); // Replace function declaration with arrow function expression const arrowFunction = j.arrowFunctionExpression.from( params: path.node.params, body: path.node.body, async: path.node.async, ); // Replace the node with the new node, preserving comments replaceWithComments(path, arrowFunction); ); return root.toSource(); Using the Studio's AI assistance, we generated a codemod to handle this transformation. The generated codemod served as a base for the final one called mocha/vitest/migrate-tests . We refined and improved it based on the edge cases we discovered during testing. We also built a second codemod called mocha/vitest/migrate-configurations that removes all Mocha.js configuration files, drops the library from all package dependencies, and adds Vitest instead. Both codemods were combined into a recipe called mocha/vitest/recipe which can be discovered, shared, and run from the Codemod Registry. Run the codemod Run the recipe with a single CLI command: npx codemod mocha/vitest/recipe We ran the recipe over 3 of our own repositories: codemod-com/filemod codemod-com/codemod-engine-node codemod-com/codemod-registry The first two had a handful of Mocha.js test suites and we migrated them first to spot problems early in the development. Once the codemod was ready and accurate, we merged them into the Codemod Registry. Once codemods are published to the registry, they automatically become accessible to the community via Codemod.com VSCode Extension or the Codemod.com CLI. And shown below, with the help of our codemod, we automated the bulk of the migration for over 60 test files spread across the entire Codemod Registry monorepo. We repeated the same steps for the Codemod Engine Node. During testing, we found out Vitest does not transpile TypeScript files if we use them as entry points for thread workers. This functionality used to work with Mocha.js supported by the ts-node/esm dependency. We replaced the path to the TS file with a path to the built ESM files to restore the feature. Final tweaks When migrating the Codemod Registry, we found unhandled cases, like: the usages of the Context type from Mocha.js, the usages of the this variable in test bodies. We could fix the former problem by putting the existing codemod into the Codemod Studio and asking the AI helper to fix it. With the latter, we fixed the test bodies manually due to the small number of such occurrences. Since the Codemod.com CLI can execute local codemods, we used it to see if the changes to the codemod resulted in the correct transformation of the codebase in question. This speeded up the development process tremendously. The impact With the help of the recipe, we were able to migrate three repositories in a short amount of time. We reused the same code within the recipe for all the projects. Now, other developers may leverage our work as well. If we were to manually migrate the Codemod Registry alone, we would have to: delete the 60+ existing Mocha.js configuration files (1 minute for each), remove Mocha.js and add Vitest dependencies in 60+ package files (1 minute for each), drop Mocha.js types and put the Vitest import statements in 60+ tests files (2 minutes for each). It would have taken around 4 hours of mundane work to perform the migration without ensuring we had not introduced any mistakes. After ensuring the proper quality of the code automation agents, we executed the recipe that transformed over 200 files instantly. We removed all Mocha.js configuration files and thus reduced the complexity of the test suites. Our platform made it all available at the push of a button. Conclusion This article walked through migrating from Mocha to Vitest using automated codemods. Key takeaways: Codemods are powerful code transformation tools that can automate tedious migration work at any scale. AI-assisted codemod creation through Codemod Studio and Codemod MCP lets any developer build codemods quickly. Open source : With Codemod's platform, you can automate crucial yet tedious tasks for yourself, your colleagues, and the community.