diff --git a/src/main/kotlin/org/openrewrite/RecipeMarkdownGenerator.kt b/src/main/kotlin/org/openrewrite/RecipeMarkdownGenerator.kt index 6ee54ede..d2c93011 100644 --- a/src/main/kotlin/org/openrewrite/RecipeMarkdownGenerator.kt +++ b/src/main/kotlin/org/openrewrite/RecipeMarkdownGenerator.kt @@ -292,6 +292,7 @@ class RecipeMarkdownGenerator : Runnable { createModerneRecipes(outputPath, moderneProprietaryRecipes) createRecipesWithDataTables(recipesWithDataTables, outputPath) createScanningRecipes(allRecipes.filter { it is ScanningRecipe<*> && it !is DeclarativeRecipe }, outputPath) + createRecipesByTag(allRecipeDescriptors, outputPath) createStandaloneRecipes(allRecipeDescriptors, outputPath) // Write the README.md for each category @@ -1126,7 +1127,8 @@ import TabItem from '@theme/TabItem'; } else if (tag.lowercase().startsWith("rspec-")) { writeln("* [$tag](https://sonarsource.github.io/rspec/#/rspec/S${tag.substring(6)})") } else { - writeln("* $tag") + val tagAnchor = tag.lowercase().replace(" ", "-") + writeln("* [$tag](../recipes-by-tag#${tagAnchor})") } } newLine() @@ -2385,6 +2387,59 @@ $cliSnippet } } + private fun createRecipesByTag(allRecipeDescriptors: List, outputPath: Path) { + val tagToRecipes = TreeMap>(String.CASE_INSENSITIVE_ORDER) + + // Collect all tags and their associated recipes + for (recipeDescriptor in allRecipeDescriptors) { + for (tag in recipeDescriptor.tags) { + tagToRecipes.computeIfAbsent(tag) { TreeSet(compareBy { it.name }) } + .add(recipeDescriptor) + } + } + + val markdown = outputPath.resolve("recipes-by-tag.md") + Files.newBufferedWriter(markdown, StandardOpenOption.CREATE).useAndApply { + writeln( + //language=markdown + """ + --- + description: An autogenerated list of all recipe tags and the recipes within each tag. + --- + + # Recipes by Tag + + _This doc contains all recipe tags and the recipes that are tagged with them._ + + """.trimIndent() + ) + + if (tagToRecipes.isEmpty()) { + writeln("No tagged recipes found.") + } else { + writeln("Total tags: ${tagToRecipes.size}\n") + + for ((tag, recipes) in tagToRecipes) { + writeln("## ${tag}") + writeln("\n_${recipes.size} recipe${if (recipes.size != 1) "s" else ""}_\n") + + for (recipe in recipes) { + val displayName = recipe.displayName.replace("`", "") + writeln( + "* [${displayName}](../recipes/${getRecipePath(recipe)}.md) - _${ + recipe.description.replace( + "\n", + " " + ) + }_" + ) + } + writeln("") + } + } + } + } + private fun createStandaloneRecipes(allRecipeDescriptors: List, outputPath: Path) { // Skip if there are no recipes to process if (allRecipeDescriptors.isEmpty()) { diff --git a/src/test/kotlin/org/openrewrite/RecipeMarkdownGeneratorTest.kt b/src/test/kotlin/org/openrewrite/RecipeMarkdownGeneratorTest.kt index 7530ace1..917b6b10 100644 --- a/src/test/kotlin/org/openrewrite/RecipeMarkdownGeneratorTest.kt +++ b/src/test/kotlin/org/openrewrite/RecipeMarkdownGeneratorTest.kt @@ -25,6 +25,7 @@ class RecipeMarkdownGeneratorTest { "8.x", // Rewrite "2.x", // Recipe BOM + "1.x", // Moderne Recipe BOM "6.x", // Gradle plugin "5.x", // Maven plugin )