Documentation menu

Reusable Codemods

Graph-sitter enables you to create reusable code transformations using Python functions decorated with @graph_sitter.function. These codemods can be shared, versioned, and run by your team.

Creating Codemods

The easiest way to create a new codemod is using the CLI create command:

gs create rename-function .

This creates a new codemod in your .codegen/codemods directory:

import graph_sitter
from graph_sitter import Codebase
 
@graph_sitter.function("rename-function")
def run(codebase: Codebase):
    """Add a description of what this codemod does."""
    # Add your code here
    pass

Codemods are stored in .codegen/codemods/name/name.py and are tracked in Git for easy sharing.

Running Codemods

Once created, run your codemod using:

gs run rename-function

The execution flow:

  1. Graph-sitter parses your codebase into a graph representation
  2. Your codemod function is executed against this graph
  3. Changes are tracked and applied to your filesystem
  4. A diff preview shows what changed

Codemod Structure

A codemod consists of three main parts:

  1. The @graph_sitter.function decorator that names your codemod
  2. A run function that takes a Codebase parameter
  3. Your transformation logic using the Codebase API
import graph_sitter
from graph_sitter import Codebase
 
@graph_sitter.function("update-imports")
def run(codebase: Codebase):
    """Update import statements to use new package names."""
    for file in codebase.files:
        for imp in file.imports:
            if imp.module == "old_package":
                imp.rename("new_package")
    codebase.commit()

Arguments

Codemods can accept arguments using Pydantic models:

from pydantic import BaseModel
 
class RenameArgs(BaseModel):
    old_name: str
    new_name: str
 
@graph_sitter.function("rename-function")
def run(codebase: Codebase, arguments: RenameArgs):
    """Rename a function across the codebase."""
    old_func = codebase.get_function(arguments.old_name)
    if old_func:
        old_func.rename(arguments.new_name)
    codebase.commit()

Run it with:

gs run rename-function --arguments '{"old_name": "getUserData", "new_name": "fetchUserProfile"}'

Directory Structure

Your codemods live in a dedicated directory structure:

.codegen/
└── codemods/
    └── rename_function/
        └── rename_function.py       # The codemod implementation