diff --git a/renderer/src/main/kotlin/location/MarkdownLocationProvider.kt b/renderer/src/main/kotlin/location/MarkdownLocationProvider.kt index 1cad637..c3cea36 100644 --- a/renderer/src/main/kotlin/location/MarkdownLocationProvider.kt +++ b/renderer/src/main/kotlin/location/MarkdownLocationProvider.kt @@ -7,8 +7,6 @@ package opensavvy.dokka.material.mkdocs.location import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProvider import org.jetbrains.dokka.base.resolvers.local.LocationProvider import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory -import org.jetbrains.dokka.pages.ModulePageNode -import org.jetbrains.dokka.pages.PageNode import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.DokkaContext @@ -17,48 +15,8 @@ class MarkdownLocationProvider( dokkaContext: DokkaContext, ) : DokkaLocationProvider(pageGraphRoot, dokkaContext, ".md") { - init { - println("Path index:") - for ((page, path) in pathsIndex) { - println(" • ${page.name} -> ${path.joinToString("/")}") - } - } - - override fun pathTo(node: PageNode, context: PageNode?): String { - val default = super.pathTo(node, context) - - return when (node) { - is ModulePageNode -> "${dokkaContext.configuration.moduleName.map { if (it.isUpperCase()) "-${it.lowercase()}" else it }.joinToString("")}/$default" - else -> { - return default - error( - """ - *** Found unexpected page type *** - Page graph root: ${pageGraphRoot.pretty()} - Module name: ${dokkaContext.configuration.moduleName} - Node: ${node.pretty()} - Context: ${context?.pretty()} - Default path: $default - """.trimIndent() - ) - } - }.also { - println() - println( - """ - Generated a path: - Path for: ${node.pretty()} - Context: ${context?.pretty()} - Generated path: $it - """.trimIndent() - ) - } - } - class Factory(private val context: DokkaContext) : LocationProviderFactory { override fun getLocationProvider(pageNode: RootPageNode): LocationProvider = MarkdownLocationProvider(pageNode, context) } } - -private fun PageNode.pretty() = "$name (${this::class})" -- 2.51.2 From 0745170d3b31612134d98288572e40924ec8571f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Wed, 1 Oct 2025 16:27:01 +0200 Subject: [PATCH 02/16] build: Create the :aggregator module --- aggregator/README.md | 9 ++++ aggregator/build.gradle.kts | 42 +++++++++++++++++++ .../src/main/kotlin/DokkaMkDocsPlugin.kt | 3 +- gradle/libs.versions.toml | 2 + settings.gradle.kts | 1 + 5 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 aggregator/README.md create mode 100644 aggregator/build.gradle.kts diff --git a/aggregator/README.md b/aggregator/README.md new file mode 100644 index 0000000..0138d5c --- /dev/null +++ b/aggregator/README.md @@ -0,0 +1,9 @@ +# Module Dokka aggregator for Material for MkDocs + +Dokka plugin that combines multiple modules generated with the Material for MkDocs format. + + + + + +This plugin is usually not used directly. Instead, use one of the Gradle plugins to configure Dokka. diff --git a/aggregator/build.gradle.kts b/aggregator/build.gradle.kts new file mode 100644 index 0000000..5658211 --- /dev/null +++ b/aggregator/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + alias(opensavvyConventions.plugins.base) + kotlin("jvm") + id("maven-publish") + alias(opensavvyConventions.plugins.kotlin.abstractLibrary) +} + +java { + withSourcesJar() +} + +kotlin { + jvmToolchain(8) +} + +dependencies { + implementation(libs.kotlinx.coroutines) + implementation(libs.dokka.base) + implementation(libs.dokka.core) + implementation(libs.dokka.templating) + implementation(libs.dokka.allModulesPage) + implementation(projects.renderer) +} + +publishing { + publications { + register("aggregator") { + from(components["java"]) + } + } +} + +library { + name.set("Dokka aggregator for Material for MkDocs") + description.set("Dokka plugin that combines multiple modules generated with the Material for MkDocs format") + homeUrl.set("https://dokka-mkdocs.opensavvy.dev/") + + license.set { + name.set("Apache 2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + } +} diff --git a/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt b/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt index 4611b0c..1e2bebc 100644 --- a/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt +++ b/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt @@ -21,8 +21,9 @@ abstract class DokkaMkDocsPlugin : DokkaFormatPlugin(formatName = "mkdocs") { override fun DokkaFormatPluginContext.configure() { project.dependencies { dokkaPlugin("dev.opensavvy.dokka.mkdocs:renderer:$DokkaMkDocsVersion") + dokkaPlugin("dev.opensavvy.dokka.mkdocs:aggregator:$DokkaMkDocsVersion") } - + moduleOutputFiles = formatDependencies.moduleOutputDirectories.incomingArtifactFiles } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bf35ea1..910860a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,6 +11,8 @@ kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", dokka-base = { module = "org.jetbrains.dokka:dokka-base", version.ref = "dokka" } dokka-core = { module = "org.jetbrains.dokka:dokka-core", version.ref = "dokka" } +dokka-templating = { module = "org.jetbrains.dokka:templating-plugin", version.ref = "dokka" } +dokka-allModulesPage = { module = "org.jetbrains.dokka:all-modules-page-plugin", version.ref = "dokka" } gradle-dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" } diff --git a/settings.gradle.kts b/settings.gradle.kts index db0b507..caeece7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -59,6 +59,7 @@ plugins { } include( + "aggregator", "renderer", "dokka-mkdocs", -- 2.51.2 From a23e462037e8ad1ca6bb07bb7ae3bfc8b8037abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Wed, 1 Oct 2025 16:52:37 +0200 Subject: [PATCH 03/16] feat(aggregator): Copy the GFM aggregation plugin --- .../kotlin/DokkaMkDocsAggregationPlugin.kt | 60 +++++++++++++ .../kotlin/DokkaMkDocsTemplatingStrategy.kt | 87 +++++++++++++++++++ ...rg.jetbrains.dokka.plugability.DokkaPlugin | 1 + .../src/main/kotlin/DokkaMkDocsPlugin.kt | 7 -- 4 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 aggregator/src/main/kotlin/DokkaMkDocsAggregationPlugin.kt create mode 100644 aggregator/src/main/kotlin/DokkaMkDocsTemplatingStrategy.kt create mode 100644 aggregator/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin diff --git a/aggregator/src/main/kotlin/DokkaMkDocsAggregationPlugin.kt b/aggregator/src/main/kotlin/DokkaMkDocsAggregationPlugin.kt new file mode 100644 index 0000000..d1dfe0e --- /dev/null +++ b/aggregator/src/main/kotlin/DokkaMkDocsAggregationPlugin.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +/* + * Copyright (c) 2025, OpenSavvy and contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package opensavvy.dokka.material.mkdocs.aggregator + +import opensavvy.dokka.material.mkdocs.MaterialForMkDocsPlugin +import opensavvy.dokka.material.mkdocs.location.MarkdownLocationProvider +import org.jetbrains.dokka.allModulesPage.AllModulesPagePlugin +import org.jetbrains.dokka.allModulesPage.MultimoduleLocationProvider +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.DokkaPluginApiPreview +import org.jetbrains.dokka.plugability.Extension +import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement +import org.jetbrains.dokka.templates.TemplateProcessingStrategy +import org.jetbrains.dokka.templates.TemplatingPlugin + +class DokkaMkDocsAggregationPlugin : DokkaPlugin() { + + private val allModulesPagePlugin by lazy { plugin() } + private val templateProcessingPlugin by lazy { plugin() } + private val mkdocsPlugin by lazy { plugin() } + private val dokkaBase by lazy { plugin()} + + val mkdocsTemplatingStrategy: Extension by extending { + (templateProcessingPlugin.templateProcessingStrategy + providing ::DokkaMkDocsTemplatingStrategy + order { before(templateProcessingPlugin.fallbackProcessingStrategy) }) + } + + val mkdocsLocationProvider: Extension by extending { + dokkaBase.locationProviderFactory providing MultimoduleLocationProvider::Factory override listOf(mkdocsPlugin.locationProvider, allModulesPagePlugin.multimoduleLocationProvider) + } + + val mkdocsPartialLocationProvider: Extension by extending { + allModulesPagePlugin.partialLocationProviderFactory providing MarkdownLocationProvider::Factory override allModulesPagePlugin.baseLocationProviderFactory + } + + @OptIn(DokkaPluginApiPreview::class) + override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = + PluginApiPreviewAcknowledgement +} diff --git a/aggregator/src/main/kotlin/DokkaMkDocsTemplatingStrategy.kt b/aggregator/src/main/kotlin/DokkaMkDocsTemplatingStrategy.kt new file mode 100644 index 0000000..f36f2f6 --- /dev/null +++ b/aggregator/src/main/kotlin/DokkaMkDocsTemplatingStrategy.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +/* + * Copyright (c) 2025, OpenSavvy and contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package opensavvy.dokka.material.mkdocs.aggregator + +import opensavvy.dokka.material.mkdocs.GfmCommand +import opensavvy.dokka.material.mkdocs.GfmCommand.Companion.command +import opensavvy.dokka.material.mkdocs.GfmCommand.Companion.label +import opensavvy.dokka.material.mkdocs.GfmCommand.Companion.templateCommandRegex +import opensavvy.dokka.material.mkdocs.ResolveLinkGfmCommand +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.allModulesPage.AllModulesPagePlugin +import org.jetbrains.dokka.base.templating.parseJson +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.templates.TemplateProcessingStrategy +import java.io.BufferedWriter +import java.io.File + +class DokkaMkDocsTemplatingStrategy( + val context: DokkaContext +) : TemplateProcessingStrategy { + + private val externalModuleLinkResolver = + context.plugin().querySingle { externalModuleLinkResolver } + + override fun process(input: File, output: File, moduleContext: DokkaConfiguration.DokkaModuleDescription?): Boolean = + if (input.extension == "md") { + input.bufferedReader().use { reader -> + //This should also work whenever we have a misconfigured dokka and output is pointing to the input + //the same way that html processing does + if (input.absolutePath == output.absolutePath) { + context.logger.info("Attempting to process MkDocs templates in place for directory $input, this suggests miss configuration.") + val lines = reader.readLines() + output.bufferedWriter().use { writer -> + lines.forEach { line -> + writer.processAndWrite(line, output) + } + } + } else { + output.bufferedWriter().use { writer -> + reader.lineSequence().forEach { line -> + writer.processAndWrite(line, output) + } + } + } + } + true + } else false + + private fun BufferedWriter.processAndWrite(line: String, output: File) = + processLine(line, output).run { + write(this) + newLine() + } + + private fun processLine(line: String, output: File): String = + line.replace(templateCommandRegex) { + when (val command = parseJson(it.command)) { + is ResolveLinkGfmCommand -> resolveLink(output, command.dri, it.label) + } + } + + private fun resolveLink(fileContext: File, dri: DRI, label: String): String = + externalModuleLinkResolver.resolve(dri, fileContext)?.let { address -> + "[$label]($address)" + } ?: label +} diff --git a/aggregator/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/aggregator/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 0000000..e54053e --- /dev/null +++ b/aggregator/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +opensavvy.dokka.material.mkdocs.aggregator.DokkaMkDocsAggregationPlugin diff --git a/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt b/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt index 1e2bebc..e5b6cbf 100644 --- a/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt +++ b/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt @@ -4,7 +4,6 @@ import org.gradle.api.Project import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.provider.Provider import org.gradle.api.tasks.Sync -import org.gradle.internal.execution.caching.CachingState.enabled import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.getValue import org.gradle.kotlin.dsl.provideDelegate @@ -40,12 +39,6 @@ abstract class DokkaMkDocsPlugin : DokkaFormatPlugin(formatName = "mkdocs") { from(moduleOutputFiles) into(siteOutput) - eachFile { - path = path.removePrefix("module/") - } - - exclude("includes/*") - val gitignore = File(siteOutput.asFile, ".gitignore") doLast { gitignore.writeText("*") -- 2.51.2 From f74779fdb57a6b6993a7358a8bbd7f324424b410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Fri, 13 Mar 2026 17:44:37 +0100 Subject: [PATCH 04/16] feat(aggregator): Implement the aggregator --- .../kotlin/DokkaMkDocsAggregationPlugin.kt | 20 +++++++-- .../kotlin/DokkaMkDocsTemplatingStrategy.kt | 2 +- .../MarkdownMultimoduleLocationProvider.kt | 43 +++++++++++++++++++ 3 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 aggregator/src/main/kotlin/MarkdownMultimoduleLocationProvider.kt diff --git a/aggregator/src/main/kotlin/DokkaMkDocsAggregationPlugin.kt b/aggregator/src/main/kotlin/DokkaMkDocsAggregationPlugin.kt index d1dfe0e..61ae963 100644 --- a/aggregator/src/main/kotlin/DokkaMkDocsAggregationPlugin.kt +++ b/aggregator/src/main/kotlin/DokkaMkDocsAggregationPlugin.kt @@ -23,7 +23,6 @@ package opensavvy.dokka.material.mkdocs.aggregator import opensavvy.dokka.material.mkdocs.MaterialForMkDocsPlugin import opensavvy.dokka.material.mkdocs.location.MarkdownLocationProvider import org.jetbrains.dokka.allModulesPage.AllModulesPagePlugin -import org.jetbrains.dokka.allModulesPage.MultimoduleLocationProvider import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory import org.jetbrains.dokka.plugability.DokkaPlugin @@ -38,18 +37,33 @@ class DokkaMkDocsAggregationPlugin : DokkaPlugin() { private val allModulesPagePlugin by lazy { plugin() } private val templateProcessingPlugin by lazy { plugin() } private val mkdocsPlugin by lazy { plugin() } - private val dokkaBase by lazy { plugin()} + private val dokkaBase by lazy { plugin() } + /** + * Processes GFM template commands (e.g., cross-module `ResolveLinkGfmCommand`) in generated `.md` files. + * Must be registered before the fallback strategy. + */ val mkdocsTemplatingStrategy: Extension by extending { (templateProcessingPlugin.templateProcessingStrategy providing ::DokkaMkDocsTemplatingStrategy order { before(templateProcessingPlugin.fallbackProcessingStrategy) }) } + /** + * Overrides the location provider for the aggregated publication to use Markdown (`.md`) links + * instead of the default HTML links, and to resolve cross-module references correctly. + */ val mkdocsLocationProvider: Extension by extending { - dokkaBase.locationProviderFactory providing MultimoduleLocationProvider::Factory override listOf(mkdocsPlugin.locationProvider, allModulesPagePlugin.multimoduleLocationProvider) + dokkaBase.locationProviderFactory providing MarkdownMultimoduleLocationProvider::Factory override listOf( + mkdocsPlugin.locationProvider, + allModulesPagePlugin.multimoduleLocationProvider, + ) } + /** + * Overrides the partial location provider (used when resolving cross-module links from partial outputs) + * to use Markdown (`.md`) links. + */ val mkdocsPartialLocationProvider: Extension by extending { allModulesPagePlugin.partialLocationProviderFactory providing MarkdownLocationProvider::Factory override allModulesPagePlugin.baseLocationProviderFactory } diff --git a/aggregator/src/main/kotlin/DokkaMkDocsTemplatingStrategy.kt b/aggregator/src/main/kotlin/DokkaMkDocsTemplatingStrategy.kt index f36f2f6..539199a 100644 --- a/aggregator/src/main/kotlin/DokkaMkDocsTemplatingStrategy.kt +++ b/aggregator/src/main/kotlin/DokkaMkDocsTemplatingStrategy.kt @@ -44,7 +44,7 @@ class DokkaMkDocsTemplatingStrategy( context.plugin().querySingle { externalModuleLinkResolver } override fun process(input: File, output: File, moduleContext: DokkaConfiguration.DokkaModuleDescription?): Boolean = - if (input.extension == "md") { + if (input.isFile && input.extension == "md") { input.bufferedReader().use { reader -> //This should also work whenever we have a misconfigured dokka and output is pointing to the input //the same way that html processing does diff --git a/aggregator/src/main/kotlin/MarkdownMultimoduleLocationProvider.kt b/aggregator/src/main/kotlin/MarkdownMultimoduleLocationProvider.kt new file mode 100644 index 0000000..490b655 --- /dev/null +++ b/aggregator/src/main/kotlin/MarkdownMultimoduleLocationProvider.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025, OpenSavvy and contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package opensavvy.dokka.material.mkdocs.aggregator + +import opensavvy.dokka.material.mkdocs.location.MarkdownLocationProvider +import org.jetbrains.dokka.allModulesPage.MultimoduleLocationProvider +import org.jetbrains.dokka.base.resolvers.local.LocationProvider +import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext + +/** + * A [MultimoduleLocationProvider] that generates `.md` links instead of the default `.html` ones. + * Used by [DokkaMkDocsAggregationPlugin] for the aggregation task so that cross-module links and + * the all-modules index page use proper Markdown file paths. + * + * Kept in the aggregator module (not the renderer) so that the dependency on + * `all-modules-page-plugin` is only required on the aggregation classpath. + */ +class MarkdownMultimoduleLocationProvider( + pageGraphRoot: RootPageNode, + dokkaContext: DokkaContext, +) : MultimoduleLocationProvider(pageGraphRoot, dokkaContext, ".md") { + + class Factory(private val context: DokkaContext) : LocationProviderFactory { + override fun getLocationProvider(pageNode: RootPageNode): LocationProvider = + MarkdownMultimoduleLocationProvider(pageNode, context) + } +} -- 2.51.2 From 313340faea4b3190e6a0a2f0b5f9b1eaf66cedac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Fri, 13 Mar 2026 17:46:36 +0100 Subject: [PATCH 05/16] feat(renderer): Remove the directory containing the module contents --- renderer/build.gradle.kts | 2 + .../location/MarkdownLocationProvider.kt | 41 ++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/renderer/build.gradle.kts b/renderer/build.gradle.kts index ac5b61e..8a6c01f 100644 --- a/renderer/build.gradle.kts +++ b/renderer/build.gradle.kts @@ -17,6 +17,8 @@ dependencies { implementation(libs.kotlinx.coroutines) compileOnly(libs.dokka.base) compileOnly(libs.dokka.core) + compileOnly(libs.dokka.templating) + compileOnly(libs.dokka.allModulesPage) } publishing { diff --git a/renderer/src/main/kotlin/location/MarkdownLocationProvider.kt b/renderer/src/main/kotlin/location/MarkdownLocationProvider.kt index c3cea36..e07d2d0 100644 --- a/renderer/src/main/kotlin/location/MarkdownLocationProvider.kt +++ b/renderer/src/main/kotlin/location/MarkdownLocationProvider.kt @@ -7,14 +7,53 @@ package opensavvy.dokka.material.mkdocs.location import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProvider import org.jetbrains.dokka.base.resolvers.local.LocationProvider import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory -import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext +import java.util.* class MarkdownLocationProvider( pageGraphRoot: RootPageNode, dokkaContext: DokkaContext, ) : DokkaLocationProvider(pageGraphRoot, dokkaContext, ".md") { + /** + * Overrides path building to skip the intermediate module-name directory for children of + * [ModulePageNode]. In the default Dokka behavior, module children are placed under + * `/`, which creates an undesirable extra directory level in MkDocs output. + */ + override val pathsIndex: Map> = run { + val index = IdentityHashMap>() + + fun PageNode.mkdocsPathName(): String = + if (this is PackagePageNode || this is RendererSpecificResourcePage) name + else identifierToFilename(name) + + fun registerPath(page: PageNode, prefix: List) { + when (page) { + is RootPageNode if page.forceTopLevelName -> { + index[page] = prefix + PAGE_WITH_CHILDREN_SUFFIX + page.children.forEach { registerPath(it, prefix) } + } + + is ModulePageNode -> { + index[page] = prefix + // Skip adding the module display name as a path segment for its children + page.children.forEach { registerPath(it, prefix) } + } + + else -> { + val newPrefix = prefix + page.mkdocsPathName() + index[page] = newPrefix + page.children.forEach { registerPath(it, newPrefix) } + } + } + } + + index[pageGraphRoot] = emptyList() + pageGraphRoot.children.forEach { registerPath(it, emptyList()) } + index + } + class Factory(private val context: DokkaContext) : LocationProviderFactory { override fun getLocationProvider(pageNode: RootPageNode): LocationProvider = MarkdownLocationProvider(pageNode, context) -- 2.51.2 From 85f6bd2d21b212a244bc8173b04154c96ad60b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Fri, 13 Mar 2026 17:48:48 +0100 Subject: [PATCH 06/16] feat(dokka-mkdocs): Rewrite the Gradle plugin to use the new aggregator module --- dokka-mkdocs/build.gradle.kts | 3 + .../src/main/kotlin/DokkaMkDocsPlugin.kt | 113 ++++++++++++++++-- 2 files changed, 109 insertions(+), 7 deletions(-) diff --git a/dokka-mkdocs/build.gradle.kts b/dokka-mkdocs/build.gradle.kts index edfc0e8..540422e 100644 --- a/dokka-mkdocs/build.gradle.kts +++ b/dokka-mkdocs/build.gradle.kts @@ -15,6 +15,7 @@ plugins { dependencies { implementation(libs.gradle.dokka) + compileOnly(kotlin("gradle-plugin")) } gradlePlugin { @@ -41,6 +42,7 @@ val embedCurrentVersion by tasks.registering { description = "Embeds the current version into the source code" val version = project.version as String + val dokkaVersion = libs.versions.dokka.get() val file = File(kotlin.sourceSets.main.get().kotlin.srcDirs.first(), "version.kt") inputs.property("version", version) @@ -52,6 +54,7 @@ val embedCurrentVersion by tasks.registering { // Generated file, do not edit package opensavvy.dokka.gradle internal const val DokkaMkDocsVersion = "$version" + internal const val DokkaVersion = "$dokkaVersion" """.trimIndent() ) } diff --git a/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt b/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt index e5b6cbf..8aabf94 100644 --- a/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt +++ b/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt @@ -1,15 +1,18 @@ package opensavvy.dokka.gradle import org.gradle.api.Project +import org.gradle.api.attributes.Attribute +import org.gradle.api.attributes.Usage import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.provider.Provider import org.gradle.api.tasks.Sync -import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.getValue -import org.gradle.kotlin.dsl.provideDelegate -import org.gradle.kotlin.dsl.registering +import org.gradle.kotlin.dsl.* +import org.jetbrains.dokka.gradle.DokkaExtension import org.jetbrains.dokka.gradle.formats.DokkaFormatPlugin import org.jetbrains.dokka.gradle.internal.InternalDokkaGradlePluginApi +import org.jetbrains.dokka.gradle.tasks.DokkaGenerateModuleTask +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import java.io.File abstract class DokkaMkDocsPlugin : DokkaFormatPlugin(formatName = "mkdocs") { @@ -20,15 +23,69 @@ abstract class DokkaMkDocsPlugin : DokkaFormatPlugin(formatName = "mkdocs") { override fun DokkaFormatPluginContext.configure() { project.dependencies { dokkaPlugin("dev.opensavvy.dokka.mkdocs:renderer:$DokkaMkDocsVersion") - dokkaPlugin("dev.opensavvy.dokka.mkdocs:aggregator:$DokkaMkDocsVersion") } - - moduleOutputFiles = formatDependencies.moduleOutputDirectories.incomingArtifactFiles + + // all-modules-page-plugin goes into dokkaMkdocsPublicationPlugin (added in apply() below). + // It is an external Maven artifact with no Dokka-specific Gradle metadata, so Gradle + // resolves it directly as a JAR without any attribute-matching issues. + + // The aggregator is a local project that also applies DokkaMkDocsPlugin. If it were added to + // dokkaMkdocsPublicationPlugin (DokkaClasspathAttribute=dokka-publication-plugins), Gradle + // would select the aggregator project's empty dokkaMkdocsPublicationPluginApiOnlyConsumable + // instead of its JAR (because that consumable matches DokkaClasspathAttribute=dokka-publication-plugins). + // + // Fix: use a separate configuration with DokkaClasspathAttribute=dokka-plugins. That value has + // no matching consumable on the aggregator project, so Gradle falls back to runtimeElements → JAR. + val aggregatorBucket = project.configurations.create("dokkaMkdocsAggregatorPlugin~internal") { + isCanBeResolved = false + isCanBeConsumed = false + } + project.dependencies.add("dokkaMkdocsAggregatorPlugin~internal", "dev.opensavvy.dokka.mkdocs:aggregator:$DokkaMkDocsVersion") + + val aggregatorResolver = project.configurations.create("dokkaMkdocsAggregatorPluginResolver~internal") { + isCanBeResolved = true + isCanBeConsumed = false + isTransitive = false + extendsFrom(aggregatorBucket) + attributes { + attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_RUNTIME)) + attribute(Attribute.of("org.jetbrains.dokka.format", String::class.java), formatName) + attribute(Attribute.of("org.jetbrains.dokka.classpath", String::class.java), "dokka-plugins") + } + } + + dokkaTasks.generatePublication.configure { + generator.pluginsClasspath.from(aggregatorResolver) + } + + moduleOutputFiles = project.layout.buildDirectory.dir("dokka/mkdocs").map { listOf(it.asFile) } } + @OptIn(InternalDokkaGradlePluginApi::class) override fun apply(target: Project) { super.apply(target) + // Add all-modules-page-plugin to the publication-only classpath. + // "dokkaMkdocsPublicationPlugin" is used only by dokkaGeneratePublicationMkdocs, + // not by the per-module dokkaGenerateModuleMkdocs task. + // This prevents all-modules-page-plugin from suppressing singleGeneration in module tasks. + // (The aggregator is handled differently in configure() above.) + target.dependencies.add("dokkaMkdocsPublicationPlugin", "org.jetbrains.dokka:all-modules-page-plugin:$DokkaVersion") + + // Use the Gradle project's leaf name (e.g. "example-core") as the module path instead of + // the full project path (e.g. "example/example-core"). This gives clean output URLs. + target.tasks.withType(DokkaGenerateModuleTask::class.java).configureEach { + modulePath.set(target.name) + } + + // Fix: DGP v2's KotlinAdapter excludes the metadata compilation when building classpath lists. + // This leaves shared KMP source sets (e.g. commonMain) with only source directories on their + // analysis classpath, causing "Unresolved reference" errors for stdlib symbols. + // Detect these source sets and supply the JVM compilation classpath. + target.plugins.withId("org.jetbrains.kotlin.multiplatform") { + fixKmpCommonSourceSetClasspath(target) + } + val siteOutput = target.layout.projectDirectory.dir("docs/api") val navOutput = target.layout.buildDirectory.file("mkdocs/navigation.yaml") @@ -36,6 +93,9 @@ abstract class DokkaMkDocsPlugin : DokkaFormatPlugin(formatName = "mkdocs") { group = GROUP description = "Copies the Dokkatoo pages into the website." + val dokkaTasks = target.tasks.matching { it.name.startsWith("dokkaGenerate") && it.name.endsWith("Mkdocs") } + dependsOn(dokkaTasks) + from(moduleOutputFiles) into(siteOutput) @@ -155,6 +215,42 @@ abstract class DokkaMkDocsPlugin : DokkaFormatPlugin(formatName = "mkdocs") { } } + @OptIn(InternalDokkaGradlePluginApi::class) + private fun fixKmpCommonSourceSetClasspath(target: Project) { + val kmpExtension = target.extensions.getByType(KotlinMultiplatformExtension::class.java) + val dokkaExtension = target.extensions.getByType(DokkaExtension::class.java) + + // configureEach fires lazily during task-graph resolution, after all build files have been + // evaluated, so kmpExtension.targets and compilations are fully populated at that point. + dokkaExtension.dokkaSourceSets.configureEach { + val ksName = name + + // Non-metadata main compilations (e.g. jvmMain, jsMain) + val nonMetadataMain = kmpExtension.targets + .filter { it.platformType != KotlinPlatformType.common } + .mapNotNull { it.compilations.findByName("main") } + if (nonMetadataMain.isEmpty()) return@configureEach + + // Source sets directly owned by a compilation already have a classpath from DGP + val ownedNames = nonMetadataMain + .flatMapTo(mutableSetOf()) { it.kotlinSourceSets.map { s -> s.name } } + if (ksName in ownedNames) return@configureEach + + // For a shared/common source set, find the compilations that include it transitively + val kss = kmpExtension.sourceSets.findByName(ksName) ?: return@configureEach + val compilation = nonMetadataMain + .filter { kss in it.allKotlinSourceSets } + .let { list -> + // Prefer JVM — its stdlib JAR also contains common stdlib declarations + list.firstOrNull { it.target.platformType == KotlinPlatformType.jvm } + ?: list.firstOrNull() + } + ?: return@configureEach + + classpath.from(compilation.compileDependencyFiles) + } + } + companion object { private const val startMarker = "# !!! EMBEDDED DOKKA START, DO NOT COMMIT !!! #" private const val endMarker = "# !!! EMBEDDED DOKKA END, DO NOT COMMIT !!! #" @@ -163,6 +259,9 @@ abstract class DokkaMkDocsPlugin : DokkaFormatPlugin(formatName = "mkdocs") { } fun String.decodeAsDokkaUrl(): String { + if (this.contains('-') && !this.startsWith("-")) { + return this.split('-').joinToString(" ") { it.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() } } + } var result = this var index: Int while (result.indexOf('-').also { index = it } >= 0) { -- 2.51.2 From d2aa20b041affe458509d2e27724ed7eb7127f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Sat, 14 Mar 2026 12:44:34 +0100 Subject: [PATCH 07/16] fix(dokka-mkdocs): Fix the order of the generated directories in the navigation bar --- dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt b/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt index 8aabf94..d4c49ec 100644 --- a/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt +++ b/dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt @@ -134,9 +134,14 @@ abstract class DokkaMkDocsPlugin : DokkaFormatPlugin(formatName = "mkdocs") { val bR = b.relativeTo(root) when { - aR.isDirectory && aR in bR -> -1 - bR.isDirectory && bR in aR -> 1 - else -> aR.path.replace(File.separatorChar, '\u0001').decodeAsDokkaUrl().compareTo(bR.path.replace(File.separatorChar, '\u0001').decodeAsDokkaUrl()) + a.isDirectory && b in a -> -1 + b.isDirectory && a in b -> 1 + else -> { + // \u00001 because it's the smallest invisible character, so directories will be listed first + val aPath = aR.path.split(File.separatorChar).joinToString("\u0001") { it.decodeAsDokkaUrl() } + val bPath = bR.path.split(File.separatorChar).joinToString("\u0001") { it.decodeAsDokkaUrl() } + aPath.compareTo(bPath) + } } } .forEach { file -> -- 2.51.2 From 043b483909b6872ce61024a7176a807375061c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Sat, 14 Mar 2026 20:05:37 +0100 Subject: [PATCH 08/16] build(junie): Create basic guidelines for Junie --- .junie/guidelines.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .junie/guidelines.md diff --git a/.junie/guidelines.md b/.junie/guidelines.md new file mode 100644 index 0000000..704713a --- /dev/null +++ b/.junie/guidelines.md @@ -0,0 +1,14 @@ +# Dokka for Material for MkDocs + +This project is a Dokka plugin to embed Kotlin documentation into Material for MkDocs. + +Structure: + +- `renderer/src/main/kotlin/MaterialForMkDocsPlugin.kt`: Dokka plugin that generates MkDocs-style Markdown pages +- `dokka-mkdocs/src/main/kotlin/DokkaMkDocsPlugin.kt`: Gradle plugin that configures the Dokka plugin and generates the MkDocs navigation bar +- `docs/example/example-core/src/commonMain/kotlin`: Example Kotlin code for testing the plugin +- `docs/website/docs/api`: Location of the generated Markdown for the example + +You can regenerate the example output by running `./gradlew :website:embedDokkaIntoMkDocs -p docs`. + +Users should not require editing their `mkdocs.yml` or MkDocs overrides to be able to use this project. -- 2.51.2 From ed5f3ff36c895f72d316bfbb9747e66fd7ebc71f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Sat, 14 Mar 2026 21:25:21 +0100 Subject: [PATCH 09/16] docs(website): Update the demo link --- docs/website/docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/website/docs/index.md b/docs/website/docs/index.md index 0b8d593..b6c0956 100644 --- a/docs/website/docs/index.md +++ b/docs/website/docs/index.md @@ -7,7 +7,7 @@ template: home.html Dokka in Material for MkDocs is a Gradle plugin using Dokka to extract documentation comments (KDoc) and embed them in a generated website. - [**Get started**](setup.md) -- [**Visit the demo**](api/-library%20module/index.md) +- [**Visit the demo**](api/example-core/index.md) This plugin should work for any kind of project supported by Dokka: Kotlin/JVM, Kotlin/Multiplatform, etc. This also includes extracting Java source comments from mixed Java/Kotlin projects. -- 2.51.2 From 57ae5ccfe3e21c48caacba121128fa80d747e1ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Tue, 17 Mar 2026 19:40:42 +0100 Subject: [PATCH 10/16] ci(gitlab): Force the installation of the latest JDK --- .gitlab-ci.main.kts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.gitlab-ci.main.kts b/.gitlab-ci.main.kts index f0a581c..a9bf4c4 100755 --- a/.gitlab-ci.main.kts +++ b/.gitlab-ci.main.kts @@ -197,6 +197,11 @@ gitlabCi { val documentKtMongo by job(stage = build) { opensavvyImage("mkdocs") + // TODO: remove after https://gitlab.com/opensavvy/automation/containers/-/merge_requests/33 + beforeScript { + shell("pacman -Syuu --noconfirm jdk-openjdk jdk17-openjdk") + } + script { shell("git clone --depth=1 https://gitlab.com/opensavvy/ktmongo.git /tmp/ktmongo") shell("cd /tmp/ktmongo") @@ -247,6 +252,11 @@ gitlabCi { opensavvyImage("mkdocs") variable("GIT_DEPTH", "0") + // TODO: remove after https://gitlab.com/opensavvy/automation/containers/-/merge_requests/33 + beforeScript { + shell("pacman -Syuu --noconfirm jdk-openjdk") + } + beforeScript { shell("./docs/website/verify-marker.sh") shell($$"./gradlew -p docs :website:embedDokkaIntoMkDocs -PappVersion=$project_version") -- 2.51.2 From 1eaa46a5defa539d98c162e3d2c5791147cabf1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Tue, 17 Mar 2026 19:49:05 +0100 Subject: [PATCH 11/16] build(gradle): Accept any JVM This is important to allow Gradle to use the ArchLinux-installed JVM which is already in our CI images. --- docs/gradle/gradle-daemon-jvm.properties | 1 - gradle/gradle-daemon-jvm.properties | 1 - 2 files changed, 2 deletions(-) diff --git a/docs/gradle/gradle-daemon-jvm.properties b/docs/gradle/gradle-daemon-jvm.properties index c0f7bef..06be396 100644 --- a/docs/gradle/gradle-daemon-jvm.properties +++ b/docs/gradle/gradle-daemon-jvm.properties @@ -8,5 +8,4 @@ toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/faa12903720d410 toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect -toolchainVendor=ADOPTIUM toolchainVersion=25 diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties index c0f7bef..06be396 100644 --- a/gradle/gradle-daemon-jvm.properties +++ b/gradle/gradle-daemon-jvm.properties @@ -8,5 +8,4 @@ toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/faa12903720d410 toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect -toolchainVendor=ADOPTIUM toolchainVersion=25 -- 2.51.2 From bbeff78202a17bc13d05738c5db00e69c4858436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Tue, 17 Mar 2026 19:53:40 +0100 Subject: [PATCH 12/16] ci(gitlab): Log the available Java toolchains --- .gitlab-ci.main.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.main.kts b/.gitlab-ci.main.kts index a9bf4c4..d5a2ca8 100755 --- a/.gitlab-ci.main.kts +++ b/.gitlab-ci.main.kts @@ -205,6 +205,7 @@ gitlabCi { script { shell("git clone --depth=1 https://gitlab.com/opensavvy/ktmongo.git /tmp/ktmongo") shell("cd /tmp/ktmongo") + shell("./gradlew javaToolchains") shell($$"./gradlew docs:website:embedDokkaIntoMkDocs -PappVersion=$project_version --include-build=$CI_PROJECT_DIR") shell("cd docs/website") shell("ls") -- 2.51.2 From 28f0d26844cea1d78bdc9ab0bb464742aabc4105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Tue, 17 Mar 2026 19:59:04 +0100 Subject: [PATCH 13/16] ci(gitlab): Forbid KtMongo from using its configured toolchains --- .gitlab-ci.main.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.main.kts b/.gitlab-ci.main.kts index d5a2ca8..01dcade 100755 --- a/.gitlab-ci.main.kts +++ b/.gitlab-ci.main.kts @@ -205,6 +205,7 @@ gitlabCi { script { shell("git clone --depth=1 https://gitlab.com/opensavvy/ktmongo.git /tmp/ktmongo") shell("cd /tmp/ktmongo") + shell("rm /tmp/ktmongo/gradle/gradle-daemon-jvm.properties") // TODO: remove after https://gitlab.com/opensavvy/automation/containers/-/merge_requests/33 shell("./gradlew javaToolchains") shell($$"./gradlew docs:website:embedDokkaIntoMkDocs -PappVersion=$project_version --include-build=$CI_PROJECT_DIR") shell("cd docs/website") -- 2.51.2 From b3fb28c6e4a0457be5bf52282238c51572667ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Tue, 17 Mar 2026 20:46:19 +0100 Subject: [PATCH 14/16] refactor(renderer): Migrate to the Kotlin Multiplatform plugin, with a single JVM target This allows us to use our existing Gradle conventions more easily. --- renderer/build.gradle.kts | 33 +++++++------------ .../kotlin/MaterialForMkDocsPlugin.kt | 0 .../{main => jvmMain}/kotlin/gfmTemplating.kt | 0 .../location/MarkdownLocationProvider.kt | 0 .../renderer/BriefCommentPreprocessor.kt | 0 .../kotlin/renderer/Decoration.kt | 0 .../renderer/SignatureBreaklineTransformer.kt | 0 .../kotlin/renderer3/Content.kt | 0 .../kotlin/renderer3/Decorator.kt | 0 .../kotlin/renderer3/Groups.kt | 0 .../kotlin/renderer3/Header.kt | 0 .../kotlin/renderer3/Links.kt | 0 .../kotlin/renderer3/Lists.kt | 0 .../kotlin/renderer3/MkDocsRenderer3.kt | 0 .../renderer3/MultilineSignatureStyle.kt | 0 .../kotlin/renderer3/Page.kt | 0 .../kotlin/renderer3/PlatformSpecific.kt | 0 .../kotlin/renderer3/RenderingContext.kt | 0 .../kotlin/renderer3/Tables.kt | 0 .../kotlin/renderer3/Text.kt | 0 ...rg.jetbrains.dokka.plugability.DokkaPlugin | 0 21 files changed, 12 insertions(+), 21 deletions(-) rename renderer/src/{main => jvmMain}/kotlin/MaterialForMkDocsPlugin.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/gfmTemplating.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/location/MarkdownLocationProvider.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer/BriefCommentPreprocessor.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer/Decoration.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer/SignatureBreaklineTransformer.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/Content.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/Decorator.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/Groups.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/Header.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/Links.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/Lists.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/MkDocsRenderer3.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/MultilineSignatureStyle.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/Page.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/PlatformSpecific.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/RenderingContext.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/Tables.kt (100%) rename renderer/src/{main => jvmMain}/kotlin/renderer3/Text.kt (100%) rename renderer/src/{main => jvmMain}/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin (100%) diff --git a/renderer/build.gradle.kts b/renderer/build.gradle.kts index 8a6c01f..fd95282 100644 --- a/renderer/build.gradle.kts +++ b/renderer/build.gradle.kts @@ -1,32 +1,23 @@ plugins { alias(opensavvyConventions.plugins.base) - kotlin("jvm") - id("maven-publish") - alias(opensavvyConventions.plugins.kotlin.abstractLibrary) -} - -java { - withSourcesJar() + alias(opensavvyConventions.plugins.kotlin.library) } kotlin { - jvmToolchain(8) -} + jvm() + + sourceSets.jvmMain.dependencies { + implementation(libs.kotlinx.coroutines) + compileOnly(libs.dokka.base) + compileOnly(libs.dokka.core) + compileOnly(libs.dokka.templating) + compileOnly(libs.dokka.allModulesPage) + } -dependencies { - implementation(libs.kotlinx.coroutines) - compileOnly(libs.dokka.base) - compileOnly(libs.dokka.core) - compileOnly(libs.dokka.templating) - compileOnly(libs.dokka.allModulesPage) } -publishing { - publications { - register("renderer") { - from(components["java"]) - } - } +tapmoc { + java(8) } library { diff --git a/renderer/src/main/kotlin/MaterialForMkDocsPlugin.kt b/renderer/src/jvmMain/kotlin/MaterialForMkDocsPlugin.kt similarity index 100% rename from renderer/src/main/kotlin/MaterialForMkDocsPlugin.kt rename to renderer/src/jvmMain/kotlin/MaterialForMkDocsPlugin.kt diff --git a/renderer/src/main/kotlin/gfmTemplating.kt b/renderer/src/jvmMain/kotlin/gfmTemplating.kt similarity index 100% rename from renderer/src/main/kotlin/gfmTemplating.kt rename to renderer/src/jvmMain/kotlin/gfmTemplating.kt diff --git a/renderer/src/main/kotlin/location/MarkdownLocationProvider.kt b/renderer/src/jvmMain/kotlin/location/MarkdownLocationProvider.kt similarity index 100% rename from renderer/src/main/kotlin/location/MarkdownLocationProvider.kt rename to renderer/src/jvmMain/kotlin/location/MarkdownLocationProvider.kt diff --git a/renderer/src/main/kotlin/renderer/BriefCommentPreprocessor.kt b/renderer/src/jvmMain/kotlin/renderer/BriefCommentPreprocessor.kt similarity index 100% rename from renderer/src/main/kotlin/renderer/BriefCommentPreprocessor.kt rename to renderer/src/jvmMain/kotlin/renderer/BriefCommentPreprocessor.kt diff --git a/renderer/src/main/kotlin/renderer/Decoration.kt b/renderer/src/jvmMain/kotlin/renderer/Decoration.kt similarity index 100% rename from renderer/src/main/kotlin/renderer/Decoration.kt rename to renderer/src/jvmMain/kotlin/renderer/Decoration.kt diff --git a/renderer/src/main/kotlin/renderer/SignatureBreaklineTransformer.kt b/renderer/src/jvmMain/kotlin/renderer/SignatureBreaklineTransformer.kt similarity index 100% rename from renderer/src/main/kotlin/renderer/SignatureBreaklineTransformer.kt rename to renderer/src/jvmMain/kotlin/renderer/SignatureBreaklineTransformer.kt diff --git a/renderer/src/main/kotlin/renderer3/Content.kt b/renderer/src/jvmMain/kotlin/renderer3/Content.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/Content.kt rename to renderer/src/jvmMain/kotlin/renderer3/Content.kt diff --git a/renderer/src/main/kotlin/renderer3/Decorator.kt b/renderer/src/jvmMain/kotlin/renderer3/Decorator.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/Decorator.kt rename to renderer/src/jvmMain/kotlin/renderer3/Decorator.kt diff --git a/renderer/src/main/kotlin/renderer3/Groups.kt b/renderer/src/jvmMain/kotlin/renderer3/Groups.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/Groups.kt rename to renderer/src/jvmMain/kotlin/renderer3/Groups.kt diff --git a/renderer/src/main/kotlin/renderer3/Header.kt b/renderer/src/jvmMain/kotlin/renderer3/Header.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/Header.kt rename to renderer/src/jvmMain/kotlin/renderer3/Header.kt diff --git a/renderer/src/main/kotlin/renderer3/Links.kt b/renderer/src/jvmMain/kotlin/renderer3/Links.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/Links.kt rename to renderer/src/jvmMain/kotlin/renderer3/Links.kt diff --git a/renderer/src/main/kotlin/renderer3/Lists.kt b/renderer/src/jvmMain/kotlin/renderer3/Lists.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/Lists.kt rename to renderer/src/jvmMain/kotlin/renderer3/Lists.kt diff --git a/renderer/src/main/kotlin/renderer3/MkDocsRenderer3.kt b/renderer/src/jvmMain/kotlin/renderer3/MkDocsRenderer3.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/MkDocsRenderer3.kt rename to renderer/src/jvmMain/kotlin/renderer3/MkDocsRenderer3.kt diff --git a/renderer/src/main/kotlin/renderer3/MultilineSignatureStyle.kt b/renderer/src/jvmMain/kotlin/renderer3/MultilineSignatureStyle.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/MultilineSignatureStyle.kt rename to renderer/src/jvmMain/kotlin/renderer3/MultilineSignatureStyle.kt diff --git a/renderer/src/main/kotlin/renderer3/Page.kt b/renderer/src/jvmMain/kotlin/renderer3/Page.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/Page.kt rename to renderer/src/jvmMain/kotlin/renderer3/Page.kt diff --git a/renderer/src/main/kotlin/renderer3/PlatformSpecific.kt b/renderer/src/jvmMain/kotlin/renderer3/PlatformSpecific.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/PlatformSpecific.kt rename to renderer/src/jvmMain/kotlin/renderer3/PlatformSpecific.kt diff --git a/renderer/src/main/kotlin/renderer3/RenderingContext.kt b/renderer/src/jvmMain/kotlin/renderer3/RenderingContext.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/RenderingContext.kt rename to renderer/src/jvmMain/kotlin/renderer3/RenderingContext.kt diff --git a/renderer/src/main/kotlin/renderer3/Tables.kt b/renderer/src/jvmMain/kotlin/renderer3/Tables.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/Tables.kt rename to renderer/src/jvmMain/kotlin/renderer3/Tables.kt diff --git a/renderer/src/main/kotlin/renderer3/Text.kt b/renderer/src/jvmMain/kotlin/renderer3/Text.kt similarity index 100% rename from renderer/src/main/kotlin/renderer3/Text.kt rename to renderer/src/jvmMain/kotlin/renderer3/Text.kt diff --git a/renderer/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/renderer/src/jvmMain/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin similarity index 100% rename from renderer/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin rename to renderer/src/jvmMain/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin -- 2.51.2 From 0b7fb9493328311b7e5eabfbaf73b20343056475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Tue, 17 Mar 2026 20:46:45 +0100 Subject: [PATCH 15/16] refactor(aggregator): Migrate to the Kotlin Multiplatform plugin, with a single JVM target This allows us to use our existing Gradle conventions more easily. --- aggregator/build.gradle.kts | 34 +++++++------------ .../kotlin/DokkaMkDocsAggregationPlugin.kt | 0 .../kotlin/DokkaMkDocsTemplatingStrategy.kt | 0 .../MarkdownMultimoduleLocationProvider.kt | 0 ...rg.jetbrains.dokka.plugability.DokkaPlugin | 0 5 files changed, 12 insertions(+), 22 deletions(-) rename aggregator/src/{main => jvmMain}/kotlin/DokkaMkDocsAggregationPlugin.kt (100%) rename aggregator/src/{main => jvmMain}/kotlin/DokkaMkDocsTemplatingStrategy.kt (100%) rename aggregator/src/{main => jvmMain}/kotlin/MarkdownMultimoduleLocationProvider.kt (100%) rename aggregator/src/{main => jvmMain}/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin (100%) diff --git a/aggregator/build.gradle.kts b/aggregator/build.gradle.kts index 5658211..585e623 100644 --- a/aggregator/build.gradle.kts +++ b/aggregator/build.gradle.kts @@ -1,33 +1,23 @@ plugins { alias(opensavvyConventions.plugins.base) - kotlin("jvm") - id("maven-publish") - alias(opensavvyConventions.plugins.kotlin.abstractLibrary) -} - -java { - withSourcesJar() + alias(opensavvyConventions.plugins.kotlin.library) } kotlin { - jvmToolchain(8) -} + jvm() -dependencies { - implementation(libs.kotlinx.coroutines) - implementation(libs.dokka.base) - implementation(libs.dokka.core) - implementation(libs.dokka.templating) - implementation(libs.dokka.allModulesPage) - implementation(projects.renderer) + sourceSets.jvmMain.dependencies { + implementation(libs.kotlinx.coroutines) + implementation(libs.dokka.base) + implementation(libs.dokka.core) + implementation(libs.dokka.templating) + implementation(libs.dokka.allModulesPage) + implementation(projects.renderer) + } } -publishing { - publications { - register("aggregator") { - from(components["java"]) - } - } +tapmoc { + java(8) } library { diff --git a/aggregator/src/main/kotlin/DokkaMkDocsAggregationPlugin.kt b/aggregator/src/jvmMain/kotlin/DokkaMkDocsAggregationPlugin.kt similarity index 100% rename from aggregator/src/main/kotlin/DokkaMkDocsAggregationPlugin.kt rename to aggregator/src/jvmMain/kotlin/DokkaMkDocsAggregationPlugin.kt diff --git a/aggregator/src/main/kotlin/DokkaMkDocsTemplatingStrategy.kt b/aggregator/src/jvmMain/kotlin/DokkaMkDocsTemplatingStrategy.kt similarity index 100% rename from aggregator/src/main/kotlin/DokkaMkDocsTemplatingStrategy.kt rename to aggregator/src/jvmMain/kotlin/DokkaMkDocsTemplatingStrategy.kt diff --git a/aggregator/src/main/kotlin/MarkdownMultimoduleLocationProvider.kt b/aggregator/src/jvmMain/kotlin/MarkdownMultimoduleLocationProvider.kt similarity index 100% rename from aggregator/src/main/kotlin/MarkdownMultimoduleLocationProvider.kt rename to aggregator/src/jvmMain/kotlin/MarkdownMultimoduleLocationProvider.kt diff --git a/aggregator/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/aggregator/src/jvmMain/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin similarity index 100% rename from aggregator/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin rename to aggregator/src/jvmMain/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin -- 2.51.2 From c5ef16ed580d24fb9eb50674541374579c0251cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Tue, 17 Mar 2026 20:49:27 +0100 Subject: [PATCH 16/16] build(gradle): Decrease support from Java 8 to Java 17 Gradle itself doesn't support pre-17 JVMs anymore. Since this project is a Gradle plugin, there is no need for us to support pre-17 JVMs either. --- aggregator/build.gradle.kts | 2 +- gradle/libs.versions.toml | 3 +++ renderer/build.gradle.kts | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/aggregator/build.gradle.kts b/aggregator/build.gradle.kts index 585e623..f86f4c7 100644 --- a/aggregator/build.gradle.kts +++ b/aggregator/build.gradle.kts @@ -17,7 +17,7 @@ kotlin { } tapmoc { - java(8) + java(libs.versions.java.lowestGradle.get().toInt()) } library { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 910860a..4ed4566 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,10 @@ # List of dependencies of the project [versions] +java-lowestGradle = "17" # https://docs.gradle.org/current/userguide/compatibility.html#java_runtime + coroutines = "1.10.2" # https://github.com/Kotlin/kotlinx.coroutines/releases + dokka = "2.0.0" # https://github.com/Kotlin/dokka/releases [plugins] diff --git a/renderer/build.gradle.kts b/renderer/build.gradle.kts index fd95282..093e84d 100644 --- a/renderer/build.gradle.kts +++ b/renderer/build.gradle.kts @@ -17,7 +17,7 @@ kotlin { } tapmoc { - java(8) + java(libs.versions.java.lowestGradle.get().toInt()) } library {