Skip to main content

Recipe development environment

This getting started guide covers setting up your development environment for creating your own OpenRewrite recipes.

info

If you are looking to build OpenRewrite itself instead of a specific recipe, check out our building and developing OpenRewrite guide.

Prerequisites

  • JDK (version 1.8+)
    • A JRE alone is insufficient since OpenRewrite uses compiler internals and tools only found in the JDK
  • Gradle (version 4.0+ ) or Maven (version 3.2+)
  • IntelliJ (version 2024.1+) with built-in support for OpenRewrite
    • Other IDEs might work as well, but lack dedicated support

OpenRewrite plugin in IntelliJ IDEA

We'd strongly recommend you enable the OpenRewrite plugin in IntelliJ when creating your own recipes. For more details on this, check out the video below:

Automatic project setup

The easiest way to get started developing your own recipes is to visit the rewrite-recipe-starter repository and click the "Use this template" button. That template comes already set up with all the necessary dependencies, build configuration, an example recipe, and tests of the example recipe.

If you've chosen to use the template, skip to Recipe Distribution. If you'd prefer to not use the template, please continue with the below instructions.

Manual project setup

Gradle and Maven both provide helpful commands for initializing a new project. Either of these commands will lay out an appropriate directory structure and a basic build.gradle or pom.xml file.

gradle init

Dependencies & dependency management

Rewrite provides a bill of materials (BOM) that, when imported into your build, will manage the versions of any rewrite dependencies that are included within a project.

You can import the bill of materials into either Gradle or Maven and then include concrete dependencies on the various rewrite libraries without specifying their version.

build.gradle
dependencies {
// import Rewrite's bill of materials.
implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release"))

// rewrite-java dependencies only necessary for Java Recipe development
implementation("org.openrewrite:rewrite-java")

// You only need the version that corresponds to your current
// Java version. It is fine to add all of them, though, as
// they can coexist on a classpath.
runtimeOnly("org.openrewrite:rewrite-java-8")
runtimeOnly("org.openrewrite:rewrite-java-11")
runtimeOnly("org.openrewrite:rewrite-java-17")

// rewrite-maven dependency only necessary for Maven Recipe development
implementation("org.openrewrite:rewrite-maven")

// rewrite-yaml dependency only necessary for Yaml Recipe development
implementation("org.openrewrite:rewrite-yaml")

// rewrite-properties dependency only necessary for Properties Recipe development
implementation("org.openrewrite:rewrite-properties")

// rewrite-xml dependency only necessary for XML Recipe development
implementation("org.openrewrite:rewrite-xml")

// lombok is optional, but recommended for authoring recipes
compileOnly("org.projectlombok:lombok:latest.release")
annotationProcessor("org.projectlombok:lombok:latest.release")

// For authoring tests for any kind of Recipe
testImplementation("org.openrewrite:rewrite-test")
testImplementation("org.junit.jupiter:junit-jupiter-api:latest.release")
testImplementation("org.junit.jupiter:junit-jupiter-params:latest.release")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:latest.release")
}
info

rewrite-test uses JUnit 5.

tip

rewrite-java-17, rewrite-java-11 and rewrite-java-8 can happily coexist on the same classpath. At runtime, the appropriate module for the current JDK will be selected.

Set Language Level and Bytecode Level

To enable running on the widest possible range of JDK versions, configure the Java compiler to target Java Language and Bytecode level 1.8:

For Gradle Kotlin

build.gradle.kts
tasks.named<JavaCompile>("compileJava") {
options.release.set(8)
}

For Gradle Groovy

build.gradle
// notice that we allow test source code to be compiled to release level >8
tasks.compileJava {
options.release.set(8)
}

Project layout

Having configured the project per these recommendations, you're now able to begin Recipe development. With Gradle and Maven's default project layout, you'll want to put your files in these directories:

  • src/main/java - Recipe implementations
  • src/test/java - Recipe tests
  • (optional) src/main/resources/META-INF/rewrite - YAML files for defining declarative OpenRewrite Recipes

With all of that done, your project setup is complete! You are now ready to create a Recipe.

Recipe distribution

In order for your recipes to be usable by the OpenRewrite build plugins or on app.moderne.io, they have to be published to an artifact repository.

Local publishing for testing

Before you publish your recipe module to an artifact repository, you may want to try it out locally. Run the command in the below table to publish your recipe to your local Maven repository (which typically exists at ~/.m2/repository). You can then test this recipe in a separate repository locally by following the instructions in the running your recipes section.

Run gradle publishToMavenLocal (or, equivalently, gradle pTML).

Publishing to artifact repositories

The rewrite-recipe-starter project is configured to publish to Moderne's open artifact repository (via the publishing task at the bottom of the build.gradle.kts file). If you want to publish elsewhere, you'll want to update that task. app.moderne.io can draw recipes from the provided repository, as well as from Maven Central.

info

Running the publish task will not update app.moderne.io, as only Moderne employees can add new recipes. If you want to add your recipe to app.moderne.io, please ask the team in Slack or in Discord.

These other docs might also be useful for you depending on where you want to publish the recipe:

Running your Recipes

Once your recipe module is published, either locally for testing or to an external artifact repository for broader distribution, you'll need to configure a separate repository to test with (See the Getting Started Guide for more detailed instructions). In the repository you want to test your recipe against, update the build plugins accordingly:

build.gradle
plugins {
id("java")
id("org.openrewrite.rewrite") version("6.28.1")
}

repositories {
mavenLocal()
mavenCentral()
}

dependencies {
rewrite("[your recipe module's groupId]:[your recipe module's artifactId]:[your recipe module's version]")
}

rewrite {
activeRecipe("[your recipe name]")
}
info

If testing locally, your rewrite dependency should match the structure of your .m2 folder. For example, if the path to your recipe in the .m2 folder is:

  • ~/.m2/repository/com/yourorg/rewrite-recipe-starter

and the jar in that folder is:

  • rewrite-recipe-starter-0.1.0-dev.25.uncommitted+f58d7fa

then the rewrite dependency should be:

  • rewrite("com.yourorg:rewrite-recipe-starter:0.1.0-dev.25.uncommitted+f58d7fa").
warning

If the recipe you're wanting to use is a Refaster template recipe, please keep in mind that the recipe name is not the class name – it is the generated class name. For instance, if you wanted to test the SimplifyTernary recipe - you would need to enter com.yourorg.SimplifyTernaryRecipes as the recipe name.

Now you can run your recipe with gradle rewriteRun or gradle rewriteDryRun

If you run into errors when trying to publish and read your recipe locally, try deleting the jars that end with uncommitted+<hash> and then re-build them. Furthermore, if you're getting errors in your IDE when trying to build or run recipes, try restarting the IDE.

Moderne IntelliJ plugin

Moderne offers a CLI and an IntelliJ plugin that can help speed up the development and testing of recipes. We'd strongly encourage you to check those out while developing recipes.

Next steps