From e3bd0d6515574c5f1916d9def3a4991b5f2a421e Mon Sep 17 00:00:00 2001 From: Paula Osterkamp Date: Wed, 17 Sep 2025 16:37:48 +0200 Subject: [PATCH 1/4] feat: add include isolated modules option --- README.md | 1 + .../gradle/CreateModuleGraphTask.kt | 8 ++++ .../gradle/ModuleGraphExtension.kt | 6 +++ .../modulegraph/gradle/ModuleGraphPlugin.kt | 4 ++ .../gradle/graphparser/ProjectParser.kt | 12 +++++- .../modulegraph/graph/DigraphBuilder.kt | 22 ++++++++-- .../modulegraph/graph/DigraphCodeBuilder.kt | 10 +++-- .../modulegraph/model/GraphConfig.kt | 10 +++++ .../modulegraph/ModuleGraphPluginTest.kt | 2 + .../ProjectParserRootModulesTest.kt | 40 ++++++++++++++++++- .../graph/DigraphCodeBuilderTest.kt | 20 ++++++++++ .../iurysouza/modulegraph/graph/Fixtures.kt | 13 ++++++ 12 files changed, 140 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d4df2df..fce5117 100644 --- a/README.md +++ b/README.md @@ -282,6 +282,7 @@ Optional settings: - If set, only these modules and their dependencies (direct and transitive) will be included in the graph. - If not set, all modules are considered root modules, which means the graph will include all modules and their dependencies. - This is useful when you want to focus on a specific part of your project's dependency structure. +- **includeIndependentModules**: Whether to include modules with no connections (no dependencies and no dependants) in the graph. Default is `false`. ### Multiple graphs diff --git a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/CreateModuleGraphTask.kt b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/CreateModuleGraphTask.kt index 92c5789..35b71a3 100644 --- a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/CreateModuleGraphTask.kt +++ b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/CreateModuleGraphTask.kt @@ -112,6 +112,14 @@ abstract class CreateModuleGraphTask : DefaultTask() { @get:Optional abstract val nestingEnabled: Property + @get:Input + @get:Option( + option = "includeIsolatedModules", + description = "Whether to render modules with no dependencies or dependants", + ) + @get:Optional + abstract val includeIsolatedModules: Property + @get:Input @get:Option(option = "graphModels", description = "The produced graph models") internal abstract val graphModels: ListProperty diff --git a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/ModuleGraphExtension.kt b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/ModuleGraphExtension.kt index bd83ed9..d310178 100644 --- a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/ModuleGraphExtension.kt +++ b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/ModuleGraphExtension.kt @@ -111,6 +111,12 @@ open class ModuleGraphExtension @Inject constructor(project: Project) { */ val nestingEnabled: Property = objects.property(Boolean::class.java) + /** + * Whether to include modules that have no dependencies and no dependants. + * Disabled by default. + */ + val includeIsolatedModules: Property = objects.property(Boolean::class.java) + /** * A list of additional graph configs to generate graphs for. */ diff --git a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/ModuleGraphPlugin.kt b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/ModuleGraphPlugin.kt index 189db89..21e0fda 100644 --- a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/ModuleGraphPlugin.kt +++ b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/ModuleGraphPlugin.kt @@ -43,6 +43,7 @@ open class ModuleGraphPlugin : Plugin { task.projectDirectory.set(project.layout.projectDirectory) task.strictMode.set(extension.strictMode) task.nestingEnabled.set(extension.nestingEnabled) + task.includeIsolatedModules.set(extension.includeIsolatedModules) val primaryGraphConfig = getPrimaryGraphConfig(task) val additionalGraphConfigs = task.graphConfigs.getOrElse(emptyList()) @@ -89,6 +90,7 @@ open class ModuleGraphPlugin : Plugin { val excludedModulesRegex = task.excludedModulesRegex.orNull val rootModulesRegex = task.rootModulesRegex.orNull val showFullPath = task.showFullPath.orNull + val includeIsolatedModules = task.includeIsolatedModules.orNull val strictMode = task.strictMode.orNull val nestingEnabled = task.nestingEnabled.orNull @@ -104,6 +106,7 @@ open class ModuleGraphPlugin : Plugin { excludedModulesRegex, rootModulesRegex, showFullPath, + includeIsolatedModules, strictMode, nestingEnabled, ) @@ -126,6 +129,7 @@ open class ModuleGraphPlugin : Plugin { this.linkText = linkText this.setStyleByModuleType = setStyleByModuleType this.showFullPath = showFullPath + this.includeIsolatedModules = includeIsolatedModules this.excludedConfigurationsRegex = excludedConfigurationsRegex this.excludedModulesRegex = excludedModulesRegex this.rootModulesRegex = rootModulesRegex diff --git a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/graphparser/ProjectParser.kt b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/graphparser/ProjectParser.kt index a3790b3..766e956 100644 --- a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/graphparser/ProjectParser.kt +++ b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/gradle/graphparser/ProjectParser.kt @@ -80,7 +80,7 @@ internal object ProjectParser { /** * To handle root modules we need to parse the dependency tree recursively, * starting at the root module nodes. - * This ensures we only include module nodes that are reachable from the root. + * This ensures we include all modules reachable from the root and isolated modules. * * It can happen that projects have circular dependencies. * Gradle will refuse to build such a project, but we can still render it in a graph - @@ -103,6 +103,16 @@ internal object ProjectParser { if (sourceProjectPath in projectPathsParsed) return projectPathsParsed.add(sourceProjectPath) + // Ensure the source module is present in the graph even if it has no dependencies + // Skip the Gradle root project path ":" to avoid rendering a stray colon node + if (moduleExclusionPattern.matches(sourceProjectPath).not() && sourceProjectPath != ":") { + val sourceModule = Module( + path = sourceProjectPath, + type = projectQuerier.getProjectType(sourceProjectPath, customModuleTypes), + ) + projectGraph.putIfAbsent(sourceModule, emptyList()) + } + projectQuerier.getConfigurations(sourceProjectPath).forEach { config -> val deps = config.getDirectDependencies( configExclusionPattern = configExclusionPattern, diff --git a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/graph/DigraphBuilder.kt b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/graph/DigraphBuilder.kt index 35f240e..ae73c91 100644 --- a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/graph/DigraphBuilder.kt +++ b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/graph/DigraphBuilder.kt @@ -13,14 +13,30 @@ internal object DigraphBuilder { val config = graphResult.config verifySufficientGraph(graphResult, config.strictMode) - return graphModel.flatMap { (source, targetList) -> + val edgeModels = graphModel.flatMap { (source, targetList) -> when (config.linkText) { LinkText.NONE -> targetList.distinctBy { it.path } else -> targetList }.mapNotNull { target -> buildModel(config, source, target) } - }.also { result -> + } + + val isolatedModels = if (config.includeIsolatedModules) { + val targetsSet = graphModel.values.flatten().map { it.path }.toSet() + val isolatedSources = graphModel.keys.filter { source -> + val hasNoOutgoing = graphModel[source].isNullOrEmpty() + val hasNoIncoming = !targetsSet.contains(source.path) + hasNoOutgoing && hasNoIncoming + } + isolatedSources.mapNotNull { source -> + buildModel(config, source, source) + } + } else { + emptyList() + } + + return (edgeModels + isolatedModels).also { result -> throwIfNothingMatches(result, config.focusedModulesRegex, config.strictMode) } } @@ -43,7 +59,7 @@ internal object DigraphBuilder { } val isFocusedModulesRegexSet = focusedModulesRegex != null val shouldNotAddToGraph = - sourceFullName == targetFullName || (!sourceMatches && !targetMatches) + (!sourceMatches && !targetMatches) return when { shouldNotAddToGraph -> null diff --git a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/graph/DigraphCodeBuilder.kt b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/graph/DigraphCodeBuilder.kt index d4cad51..fb1223e 100644 --- a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/graph/DigraphCodeBuilder.kt +++ b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/graph/DigraphCodeBuilder.kt @@ -27,9 +27,13 @@ internal object DigraphCodeBuilder { """.trimMargin(), ) - private fun toMermaid(it: DigraphModel, linkText: LinkText): CharSequence = """ - | ${it.source.fullPath} ${linkText.toLinkString(it.target.config.value)} ${it.target.fullPath} - """.trimMargin() + private fun toMermaid(it: DigraphModel, linkText: LinkText): CharSequence { + return if (it.source.fullPath == it.target.fullPath) { + """ ${it.source.fullPath}["${it.source.name}"]""" + } else { + """ ${it.source.fullPath} ${linkText.toLinkString(it.target.config.value)} ${it.target.fullPath}""" + } + } } private fun LinkText.toLinkString(configName: String?): String = when (this) { diff --git a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/model/GraphConfig.kt b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/model/GraphConfig.kt index ba0e8e4..f51e689 100644 --- a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/model/GraphConfig.kt +++ b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/model/GraphConfig.kt @@ -46,6 +46,12 @@ data class GraphConfig( */ val showFullPath: Boolean, + /** + * Whether to include modules that have no dependencies and no dependants. + * Disabled by default. + */ + val includeIsolatedModules: Boolean, + /* Content regex pattern parameters */ /** @@ -111,6 +117,9 @@ data class GraphConfig( /** @see [GraphConfig.showFullPath] */ var showFullPath: Boolean? = null + /** @see [GraphConfig.includeIsolatedModules] */ + var includeIsolatedModules: Boolean? = null + /** @see [GraphConfig.excludedConfigurationsRegex] */ var excludedConfigurationsRegex: String? = null @@ -146,6 +155,7 @@ data class GraphConfig( excludedModulesRegex = excludedModulesRegex, rootModulesRegex = rootModulesRegex, showFullPath = showFullPath ?: false, + includeIsolatedModules = includeIsolatedModules ?: false, strictMode = strictMode ?: false, nestingEnabled = nestingEnabled ?: false, ) diff --git a/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/ModuleGraphPluginTest.kt b/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/ModuleGraphPluginTest.kt index cbce260..b749492 100644 --- a/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/ModuleGraphPluginTest.kt +++ b/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/ModuleGraphPluginTest.kt @@ -43,6 +43,7 @@ class ModuleGraphPluginTest { excludedModulesRegex.set("project") focusedModulesRegex.set(".*test.*") rootModulesRegex.set(".*") + includeIsolatedModules.set(true) } val task = project.tasks.getByName("createModuleGraph") as CreateModuleGraphTask @@ -57,5 +58,6 @@ class ModuleGraphPluginTest { assertEquals("implementation", task.excludedConfigurationsRegex.get()) assertEquals("project", task.excludedModulesRegex.get()) assertEquals(".*", task.rootModulesRegex.get()) + assertEquals(true, task.includeIsolatedModules.get()) } } diff --git a/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/ProjectParserRootModulesTest.kt b/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/ProjectParserRootModulesTest.kt index 35160a2..95ea4cf 100644 --- a/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/ProjectParserRootModulesTest.kt +++ b/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/ProjectParserRootModulesTest.kt @@ -25,6 +25,7 @@ internal class ProjectParserRootModulesTest { ModuleToDeps.commonData, ModuleToDeps.coreNetworking, ModuleToDeps.coreUtil, + ModuleToDeps.isolated, ) private val projectQuerier = object : ProjectQuerier { @@ -45,7 +46,16 @@ internal class ProjectParserRootModulesTest { @Test fun `correct graph when root module is app`() { - val expectedGraph = entireGraph + val expectedGraph = mapOf( + ModuleToDeps.app, + ModuleToDeps.featAUi, + ModuleToDeps.commonComponent, + ModuleToDeps.coreUi, + ModuleToDeps.featAData, + ModuleToDeps.commonData, + ModuleToDeps.coreNetworking, + ModuleToDeps.coreUtil, + ) val actualGraph = ProjectParser.parseProjectGraph( allProjectPaths = Project.allPaths, config = getConfig(rootModulesRegex = MockProjectPath.app, theme = theme), @@ -122,6 +132,25 @@ internal class ProjectParserRootModulesTest { Assertions.assertEquals(expectedGraph, actualGraph) } + + @Test + fun `correct graph when include isolated modules is true`() { + val expectedGraph = entireGraph + ModuleToDeps.isolated + val actualGraph = ProjectParser.parseProjectGraph( + allProjectPaths = Project.allPaths, + config = getConfig(rootModulesRegex = null, theme = theme, includeIsolatedModules = true), + projectQuerier = projectQuerier, + ) + + Assertions.assertEquals(expectedGraph, actualGraph) + val isolatedModule = Module( + path = MockProjectPath.isolated, + type = Default.moduleType, + configName = null, + ) + Assertions.assertTrue(actualGraph.containsKey(isolatedModule)) + Assertions.assertTrue(actualGraph[isolatedModule]?.isEmpty() == true) + } } private object Default { @@ -138,6 +167,7 @@ private object MockProjectPath { const val coreUtil = ":core:util" const val commonData = ":common:data" const val coreNetworking = ":core:networking" + const val isolated = ":isolated" } private data class ProjectAndDeps(val path: ProjectPath, val deps: List) @@ -181,6 +211,11 @@ private object Project { ), ) + val isolated = ProjectAndDeps( + MockProjectPath.isolated, + emptyList(), + ) + val all = listOf( coreUtil, coreNetworking, @@ -190,6 +225,7 @@ private object Project { featAUi, featAData, app, + isolated, ) val allPaths = all.map { it.path } @@ -223,6 +259,8 @@ private object ModuleToDeps { createDefaultModuleTarget(MockProjectPath.coreNetworking), ) + val isolated = createDefaultModuleSource(MockProjectPath.isolated) to emptyList() + private fun createDefaultModuleSource(path: String) = Module( path = path, type = Default.moduleType, diff --git a/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/graph/DigraphCodeBuilderTest.kt b/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/graph/DigraphCodeBuilderTest.kt index 5fb16b2..2d667e2 100644 --- a/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/graph/DigraphCodeBuilderTest.kt +++ b/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/graph/DigraphCodeBuilderTest.kt @@ -78,4 +78,24 @@ class DigraphCodeBuilderTest { """.trimMargin() assertEquals(expectedMermaidCode, mermaidCode.value) } + + @Test + fun `Build digraph with include isolated modules returns correct graph`() { + val graphModel = graphWithIsolatedModules() + val config = getConfig(includeIsolatedModules = true) + val result = GraphParseResult(graphModel, config) + + val mermaidCode = DigraphCodeBuilder.build( + digraphModel = DigraphBuilder.build(result), + linkText = config.linkText, + ) + + val expected = """ + | :app --> :utilities + | :group:child1 --> :group:child2 + | :isolated["isolated"] + | :group:lonely["lonely"] + """.trimMargin() + assertEquals(expected, mermaidCode.value) + } } diff --git a/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/graph/Fixtures.kt b/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/graph/Fixtures.kt index b05920d..bf470f0 100644 --- a/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/graph/Fixtures.kt +++ b/plugin-build/modulegraph/src/test/java/dev/iurysouza/modulegraph/graph/Fixtures.kt @@ -46,6 +46,17 @@ internal fun aModuleGraph() = mapOf( ), ) +internal fun graphWithIsolatedModules() = linkedMapOf( + Module(path = ":app") to listOf( + Module(path = ":utilities", configName = "implementation"), + ), + Module(path = ":group:child1") to listOf( + Module(path = ":group:child2", configName = "implementation"), + ), + Module(path = ":isolated") to emptyList(), + Module(path = ":group:lonely") to emptyList(), +) + internal val expectedMermaidGraphCode = """ |```mermaid |%%{ @@ -84,6 +95,7 @@ internal fun getConfig( linkText: LinkText? = null, setStyleByModuleType: Boolean? = null, showFullPath: Boolean? = null, + includeIsolatedModules: Boolean? = null, strictMode: Boolean? = null, nestingEnabled: Boolean? = null, ) = @@ -100,6 +112,7 @@ internal fun getConfig( this.linkText = linkText this.setStyleByModuleType = setStyleByModuleType this.showFullPath = showFullPath + this.includeIsolatedModules = includeIsolatedModules this.strictMode = strictMode this.nestingEnabled = nestingEnabled }.build() From 4e37d5724371db2b2ce78b9b7f057787fd11746b Mon Sep 17 00:00:00 2001 From: Iury Souza Date: Tue, 23 Sep 2025 21:37:20 +0200 Subject: [PATCH 2/4] fix(DigraphBuilder): avoid self-referential dependencies in edge models --- .../dev/iurysouza/modulegraph/graph/DigraphBuilder.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/graph/DigraphBuilder.kt b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/graph/DigraphBuilder.kt index ae73c91..e5871ec 100644 --- a/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/graph/DigraphBuilder.kt +++ b/plugin-build/modulegraph/src/main/kotlin/dev/iurysouza/modulegraph/graph/DigraphBuilder.kt @@ -17,9 +17,10 @@ internal object DigraphBuilder { when (config.linkText) { LinkText.NONE -> targetList.distinctBy { it.path } else -> targetList - }.mapNotNull { target -> - buildModel(config, source, target) - } + }.filter { target -> target.path != source.path } + .mapNotNull { target -> + buildModel(config, source, target) + } } val isolatedModels = if (config.includeIsolatedModules) { From 30678fbc60d1fd4c77bc07a903affee57364d51e Mon Sep 17 00:00:00 2001 From: Paula Osterkamp Date: Mon, 13 Oct 2025 10:04:33 +0200 Subject: [PATCH 3/4] fix: update gradle-wrapper.jar --- gradle/wrapper/gradle-wrapper.jar | Bin 43504 -> 43583 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 2c3521197d7c4586c843d1d3e9090525f1898cde..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 3990 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 From 55abfb43b570b293a2d495f9295b74a6110a4109 Mon Sep 17 00:00:00 2001 From: Iury Souza Date: Tue, 14 Oct 2025 16:23:30 +0200 Subject: [PATCH 4/4] fix: update gradle wrapper validation action to v4 The old gradle/wrapper-validation-action@v1 has been deprecated in favor of gradle/actions/wrapper-validation@v4 --- .github/workflows/gradle-wrapper-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index e77cdda..5d13f8e 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -20,5 +20,5 @@ jobs: distribution: 'zulu' java-version: 8 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/actions/wrapper-validation@v4