Skip to main content

Defining the recipe marketplace with a CSV file

Starting with CLI version 3.55.0, the recipe marketplace in the Moderne CLI is defined via a CSV file rather than a binary Lucene index. This provides several benefits:

  • Faster bootstrapping - Installing recipes only requires appending entries to a CSV file rather than rebuilding a binary index.
  • Lazy JAR loading - Recipe JARs are downloaded on-demand when running recipes, rather than requiring all JARs to be present upfront.
  • Human-readable format - The marketplace can be easily inspected and debugged using standard tools.
info

From a user perspective, all CLI commands remain the same. The CSV format is an internal optimization that improves performance without changing how you interact with the CLI.

How it works

When you install a recipe JAR using mod config recipes jar install, the CLI checks whether the JAR contains an embedded recipes.csv file:

  • If present: The CLI extracts the CSV and appends its contents to your global marketplace file (see below).
  • If absent: The CLI falls back to classpath scanning to discover recipes in the JAR (the previous behavior). This is significantly slower to parse than the CSV approach.

Most OpenRewrite recipe modules now include an embedded recipes.csv file. Third-party recipe libraries that don't use the OpenRewrite build plugin will still work via the fallback mechanism.

Marketplace file location

The global recipe marketplace is stored at:

~/.moderne/cli/recipes-v5.csv

For new CLI installations, this file starts empty and grows as you install recipe JARs. For existing installations, the CLI automatically migrates your previous marketplace (v4) to the new format the first time you run any marketplace-related command (jar install, search, run, etc.).

CSV format

The recipes-v5.csv file contains one row per recipe with the following columns:

ColumnRequiredDescription
ecosystemYesThe package ecosystem identifier (e.g., maven, npm, yaml).
packageNameYesThe package coordinates (e.g., org.openrewrite.recipe:rewrite-java-dependencies).
nameYesThe fully qualified recipe name (e.g., org.openrewrite.java.dependencies.AddDependency).
displayNameNoA human-readable recipe title.
descriptionNoA brief explanation of what the recipe does.
recipeCountNoThe number of recipes (direct + transitive) if this is a composite recipe.
category1, category2, ... categoryNNoHierarchical category columns. category1 is the deepest (most specific) level; higher numbers represent broader parent categories.
category1Description, category2Description, ... categoryNDescriptionNoOptional descriptions for each corresponding category level.
optionsNoA JSON array of the recipe's configurable options and their metadata. Must match the options defined in the recipe class. Empty if the recipe defines no options.
dataTablesNoA JSON array of data tables defined by the recipe. Must match the data tables the recipe explicitly produces. Empty if the recipe defines no additional data tables beyond the default data tables.
versionNoThe resolved package version. Populated by the CLI during installation. Should not be included in JAR-embedded CSVs.
requestedVersionNoThe version constraint used when installing (e.g., LATEST). Populated by the CLI during installation. Should not be included in JAR-embedded CSVs.
teamNoAn organization identifier for multi-tenancy. When set, recipes are only visible when working within that organization's context.

Example entries

Minimal entry - Only the required columns are needed to identify a recipe:

ecosystem,packageName,name
maven,org.openrewrite:rewrite-java,org.openrewrite.java.cleanup.UnnecessaryParentheses

With metadata and categories - Includes human-readable information and hierarchical categorization:

ecosystem,packageName,name,displayName,description,recipeCount,category1,category2,requestedVersion,version
maven,org.openrewrite:rewrite-java,org.openrewrite.java.format.AutoFormat,Auto-format Java code,Formats Java source code according to style guidelines.,15,Formatting,Java,LATEST,8.45.0

With recipe options - Recipes that accept parameters include a JSON array in the options column:

ecosystem,packageName,name,displayName,description,options
maven,org.openrewrite.recipe:rewrite-java-dependencies,org.openrewrite.java.dependencies.AddDependency,Add Gradle or Maven dependency,Add a dependency to a Gradle or Maven project.,"[{""name"":""groupId"",""type"":""String"",""displayName"":""Group ID"",""description"":""The group ID of the dependency."",""required"":true},{""name"":""artifactId"",""type"":""String"",""displayName"":""Artifact ID"",""description"":""The artifact ID of the dependency."",""required"":true},{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""The version of the dependency.""}]"

For more examples, see the Recipe Marketplace CSV Format ADR.

Default data tables

All OpenRewrite recipes automatically produce the following data tables, which do not need to be listed in the dataTables column:

Data tableDescription
org.openrewrite.table.SourcesFileResultsSource files that were modified by the recipe.
org.openrewrite.table.SourcesFileErrorsSource files that had errors during recipe execution.
org.openrewrite.table.RecipeRunStatsStatistics about the recipe run.
org.openrewrite.table.ParseFailuresFiles that failed to parse.
org.openrewrite.table.SearchResultsSource files that matched a search recipe.

The dataTables column should only include data tables that the recipe explicitly defines beyond these defaults.

Curating categories

The recipes-v5.csv file can be manually adjusted to make it so that recipes can appear in multiple categories. You can do this by duplicating any existing recipe row and changing the category columns.

warning

Do not attempt to create recipe entries from scratch by hand - the options JSON is complex and should be generated by the build plugins.

There are two main reasons why you'd want to duplicate recipes and change their columns:

  • Cross-language recipes: Some recipes work across multiple languages and you may want them to appear in multiple places in the marketplace. For example, if you had a Java recipe that also worked for JavaScript repository, you could duplicate the recipe and change the category so that it appears in both places.
  • Internal recommendations: Your company may want to create custom groupings of recipes for developers. In this case, you could duplicate the recipe and add it to a specific custom category.

When duplicating rows, the recipe metadata (options, dataTables, displayName, description) must remain identical across all copies. Only the category columns should differ.

For recipe authors

If you maintain a recipe library and want to include an embedded recipes.csv file in your JAR, the OpenRewrite Gradle build plugin provides tasks to generate and validate the CSV.

Generating the CSV

To create or update your recipes.csv file, run:

./gradlew recipeCsvGenerate

This task scans your recipe JAR and generates the CSV, preserving any manual customizations you've made (such as custom category assignments).

Automatic validation

Once a recipes.csv file exists, validation runs automatically as part of the standard Gradle check task. This means every build will verify that:

  • Content formatting is correct - Display names start with uppercase, descriptions end with periods, etc.
  • The CSV is in sync with the JAR - No phantom recipes (in CSV but not in JAR) or missing recipes (in JAR but not in CSV).

If validation fails, the build will fail with detailed error messages.

For more details, see the Recipe Marketplace CSV Gradle Tasks ADR.

note

There is currently no Maven plugin equivalent. Organizations not using the Gradle plugin will need to implement similar functionality themselves, manually create and maintain a recipes.csv file, or rely on the CLI's fallback classpath scanning.

Additional resources