From 5ccc99ba35d21dae687a11b4675e56e3a14098b9 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Thu, 23 Dec 2021 11:39:16 +0100 Subject: [PATCH] Add global settings to JSON dokka cli input --- integration-tests/cli/build.gradle.kts | 3 + .../dokka/it/cli/CliIntegrationTest.kt | 111 +++++++++++++++++- .../org/jetbrains/dokka/it/cli/jsonBuilder.kt | 52 ++++++++ .../integrationTest/resources/my-file.json | 0 plugins/all-modules-page/out/index.md | 10 ++ plugins/base/frontend/package-lock.json | 2 +- runners/cli/build.gradle.kts | 4 + .../src/main/kotlin/cli/JsonMapperForCLI.kt | 48 ++++++++ runners/cli/src/main/kotlin/cli/main.kt | 39 ++++-- runners/cli/src/test/kotlin/cli/CliTest.kt | 29 +++++ runners/cli/src/test/resources/my-file.json | 51 ++++++++ 11 files changed, 337 insertions(+), 12 deletions(-) create mode 100644 integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt create mode 100644 integration-tests/cli/src/integrationTest/resources/my-file.json create mode 100644 plugins/all-modules-page/out/index.md create mode 100644 runners/cli/src/main/kotlin/cli/JsonMapperForCLI.kt create mode 100644 runners/cli/src/test/kotlin/cli/CliTest.kt create mode 100644 runners/cli/src/test/resources/my-file.json diff --git a/integration-tests/cli/build.gradle.kts b/integration-tests/cli/build.gradle.kts index d9961f8f28..778a3bd23a 100644 --- a/integration-tests/cli/build.gradle.kts +++ b/integration-tests/cli/build.gradle.kts @@ -12,6 +12,8 @@ evaluationDependsOn(":plugins:base") dependencies { implementation(kotlin("stdlib")) implementation(kotlin("test-junit")) + val jackson_version: String by project + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version") } /* Create a fat base plugin jar for cli tests */ @@ -23,6 +25,7 @@ val basePluginShadow: Configuration by configurations.creating { dependencies { basePluginShadow(project(":plugins:base")) + } val basePluginShadowJar by tasks.register("basePluginShadowJar", ShadowJar::class) { configurations = listOf(basePluginShadow) diff --git a/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt b/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt index b87badd71c..4234e9b147 100644 --- a/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt +++ b/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt @@ -2,6 +2,8 @@ package org.jetbrains.dokka.it.cli import org.jetbrains.dokka.it.awaitProcessResult import java.io.File +import java.io.PrintWriter +import java.lang.IllegalStateException import kotlin.test.* class CliIntegrationTest : AbstractCliIntegrationTest() { @@ -193,7 +195,7 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { } @Test - fun `logging level should be respected`(){ + fun `logging level should be respected`() { val dokkaOutputDir = File(projectDir, "output") assertTrue(dokkaOutputDir.mkdirs()) val process = ProcessBuilder( @@ -259,4 +261,111 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { ) ) } + + + @Test + fun json() { + val dokkaOutputDir = File(projectDir, "output") + assertTrue(dokkaOutputDir.mkdirs()) + val jsonPath = javaClass.getResource("/my-file.json")?.path ?: throw IllegalStateException("No JSON found!") + PrintWriter(jsonPath).run { + write(jsonBuilder(dokkaOutputDir.path, basePluginJarFile.path, File(projectDir, "src").path, reportUndocumented = true)) + close() + } + + val process = ProcessBuilder( + "java", "-jar", cliJarFile.path, jsonPath + ).redirectErrorStream(true).start() + + val result = process.awaitProcessResult() + assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)") + + val extensionLoadedRegex = Regex("""Extension: org\.jetbrains\.dokka\.base\.DokkaBase""") + val amountOfExtensionsLoaded = extensionLoadedRegex.findAll(result.output).count() + + assertTrue( + amountOfExtensionsLoaded > 10, + "Expected more than 10 extensions being present (found $amountOfExtensionsLoaded)" + ) + + val undocumentedReportRegex = Regex("""Undocumented:""") + val amountOfUndocumentedReports = undocumentedReportRegex.findAll(result.output).count() + assertTrue( + amountOfUndocumentedReports > 0, + "Expected at least one report of undocumented code (found $amountOfUndocumentedReports)" + ) + + assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory") + } + + /** + * This test disables global `reportUndocumneted` property and set `reportUndocumented` via perPackageOptions to + * make sure that global settings apply to dokka context. + */ + @Test + fun jsonWithGlobals() { + val dokkaOutputDir = File(projectDir, "output") + assertTrue(dokkaOutputDir.mkdirs()) + val jsonPath = javaClass.getResource("/my-file.json")?.path ?: throw IllegalStateException("No JSON found!") + PrintWriter(jsonPath).run { + write( + jsonBuilder( + outputPath = dokkaOutputDir.path, + pluginsClasspath = basePluginJarFile.path, + projectPath = File(projectDir, "src").path, + globalSourceLinks = """ + { + "localDirectory": "/home/Vadim.Mishenev/dokka/examples/cli/src/main/kotlin", + "remoteUrl": "https://github.com/Kotlin/dokka/tree/master/examples/gradle/dokka-gradle-example/src/main/kotlin", + "remoteLineSuffix": "#L" + } + """.trimIndent(), + globalExternalDocumentationLinks = """ + { + "url": "https://docs.oracle.com/javase/8/docs/api/", + "packageListUrl": "https://docs.oracle.com/javase/8/docs/api/package-list" + }, + { + "url": "https://kotlinlang.org/api/latest/jvm/stdlib/", + "packageListUrl": "https://kotlinlang.org/api/latest/jvm/stdlib/package-list" + } + """.trimIndent(), + globalPerPackageOptions = """ + { + "matchingRegex": ".*", + "skipDeprecated": "true", + "reportUndocumented": "true", + "documentedVisibilities": ["PUBLIC", "PRIVATE", "PROTECTED", "INTERNAL", "PACKAGE"] + } + """.trimIndent(), + reportUndocumented = false + ), + ) + close() + } + + val process = ProcessBuilder( + "java", "-jar", cliJarFile.path, jsonPath + ).redirectErrorStream(true).start() + + val result = process.awaitProcessResult() + assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)") + + val extensionLoadedRegex = Regex("""Extension: org\.jetbrains\.dokka\.base\.DokkaBase""") + val amountOfExtensionsLoaded = extensionLoadedRegex.findAll(result.output).count() + + assertTrue( + amountOfExtensionsLoaded > 10, + "Expected more than 10 extensions being present (found $amountOfExtensionsLoaded)" + ) + + val undocumentedReportRegex = Regex("""Undocumented:""") + val amountOfUndocumentedReports = undocumentedReportRegex.findAll(result.output).count() + assertTrue( + amountOfUndocumentedReports > 0, + "Expected at least one report of undocumented code (found $amountOfUndocumentedReports)" + ) + + assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory") + } } diff --git a/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt b/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt new file mode 100644 index 0000000000..d5d1df82e6 --- /dev/null +++ b/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt @@ -0,0 +1,52 @@ +package org.jetbrains.dokka.it.cli + +fun jsonBuilder( + outputPath: String, + pluginsClasspath: String, + projectPath: String, + globalSourceLinks: String = "", + globalExternalDocumentationLinks: String = "", + globalPerPackageOptions: String = "", + reportUndocumented: Boolean = false + +): String { + return """{ + "moduleName": "Dokka Example", + "moduleVersion": null, + "outputDir": "$outputPath", + "pluginsClasspath": ["$pluginsClasspath"], + "cacheRoot": null, + "offlineMode": false, + "sourceLinks": [$globalSourceLinks], + "externalDocumentationLinks": [$globalExternalDocumentationLinks], + "perPackageOptions": [$globalPerPackageOptions], + "sourceSets": [ + { + "displayName": "jvm", + "sourceSetID": { + "scopeId": ":dokkaHtml", + "sourceSetName": "main" + }, + "sourceRoots": [ + "$projectPath" + ], + "dependentSourceSets": [], + "samples": [], + "includes": [], + "includeNonPublic": false, + "reportUndocumented": $reportUndocumented, + "skipEmptyPackages": true, + "skipDeprecated": false, + "jdkVersion": 8, + "sourceLinks": [], + "perPackageOptions": [], + "externalDocumentationLinks": [], + "noStdlibLink": false, + "noJdkLink": false, + "suppressedFiles": [], + "analysisPlatform": "jvm" + } + ] +} +""" +} diff --git a/integration-tests/cli/src/integrationTest/resources/my-file.json b/integration-tests/cli/src/integrationTest/resources/my-file.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/all-modules-page/out/index.md b/plugins/all-modules-page/out/index.md new file mode 100644 index 0000000000..5c9593f62f --- /dev/null +++ b/plugins/all-modules-page/out/index.md @@ -0,0 +1,10 @@ +/ + +# Sample project + +Sample documentation with [external link](https://www.google.pl) + +## All modules: + +| Name | +|---| diff --git a/plugins/base/frontend/package-lock.json b/plugins/base/frontend/package-lock.json index 997f5c0355..a28d810e70 100644 --- a/plugins/base/frontend/package-lock.json +++ b/plugins/base/frontend/package-lock.json @@ -41,7 +41,7 @@ "terser-webpack-plugin": "^5.2.5" }, "engines": { - "node": ">=17.0.0" + "node": ">=16.0.0" } }, "node_modules/@babel/code-frame": { diff --git a/runners/cli/build.gradle.kts b/runners/cli/build.gradle.kts index 08fadec9e3..9e8497f626 100644 --- a/runners/cli/build.gradle.kts +++ b/runners/cli/build.gradle.kts @@ -10,6 +10,10 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.3") implementation(project(":core")) implementation(kotlin("stdlib")) + val jackson_version: String by project + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version") + + testImplementation(kotlin("test-junit")) } tasks { diff --git a/runners/cli/src/main/kotlin/cli/JsonMapperForCLI.kt b/runners/cli/src/main/kotlin/cli/JsonMapperForCLI.kt new file mode 100644 index 0000000000..33bfd9fc04 --- /dev/null +++ b/runners/cli/src/main/kotlin/cli/JsonMapperForCLI.kt @@ -0,0 +1,48 @@ +package org.jetbrains.dokka + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.io.File + +// THIS IS COPIED FROM BASE SINCE IT NEEDS TO BE INSTANTIATED ON THE SAME CLASS LOADER AS PLUGINS + +private val objectMapper = run { + val module = SimpleModule().apply { + addSerializer(FileSerializer) + } + jacksonObjectMapper() + .registerModule(module) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) +} + +@PublishedApi +internal class TypeReference private constructor( + internal val jackson: com.fasterxml.jackson.core.type.TypeReference +) { + companion object { + internal inline operator fun invoke(): TypeReference = TypeReference(jacksonTypeRef()) + } +} + +inline fun parseJson(json: String): T = parseJson(json, TypeReference()) + +@PublishedApi +internal fun parseJson(json: String, typeReference: TypeReference): T = + objectMapper.readValue(json, typeReference.jackson) + +private object FileSerializer : StdScalarSerializer(File::class.java) { + override fun serialize(value: File, g: JsonGenerator, provider: SerializerProvider) { + g.writeString(value.path) + } +} + +data class GlobalDokkaConfiguration( + val perPackageOptions: List?, + val externalDocumentationLinks: List?, + val sourceLinks: List? +) diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt index 739539a997..e8b7914635 100644 --- a/runners/cli/src/main/kotlin/cli/main.kt +++ b/runners/cli/src/main/kotlin/cli/main.kt @@ -2,10 +2,7 @@ package org.jetbrains.dokka import kotlinx.cli.* import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink -import org.jetbrains.dokka.utilities.DokkaConsoleLogger -import org.jetbrains.dokka.utilities.DokkaLogger -import org.jetbrains.dokka.utilities.LoggingLevel -import org.jetbrains.dokka.utilities.cast +import org.jetbrains.dokka.utilities.* import java.io.* import java.net.MalformedURLException import java.net.URL @@ -80,6 +77,7 @@ class GlobalArguments(args: Array) : DokkaConfiguration { private val _includes by parser.option( ArgTypeFile, + fullName = "includes", description = "Markdown files that would be displayed in multi-module page separated by the semicolon `;`)" ).delimiter(";") @@ -406,14 +404,35 @@ fun parseLinks(links: List): List { } } +fun initializeConfiguration(globalArguments: GlobalArguments): DokkaConfiguration = if (globalArguments.json != null) { + val jsonContent = Paths.get(checkNotNull(globalArguments.json)).toFile().readText() + val globals: GlobalDokkaConfiguration = parseJson(jsonContent) + val dokkaConfigurationImpl = DokkaConfigurationImpl(jsonContent) + dokkaConfigurationImpl.run { + sourceSets.forEach { + it.perPackageOptions.cast>().addAll(globals.perPackageOptions ?: emptyList()) + } + + sourceSets.forEach { + it.externalDocumentationLinks.cast>().addAll(globals.externalDocumentationLinks ?: emptyList()) + } + + sourceSets.forEach { + it.sourceLinks.cast>().addAll(globals.sourceLinks ?: emptyList()) + } + + sourceSets.forEach { + it.externalDocumentationLinks.cast>().addAll(defaultLinks(it)) + } + } + dokkaConfigurationImpl + } else { + globalArguments + } + fun main(args: Array) { val globalArguments = GlobalArguments(args) - val configuration = if (globalArguments.json != null) - DokkaConfigurationImpl( - Paths.get(checkNotNull(globalArguments.json)).toFile().readText() - ) - else - globalArguments + val configuration = initializeConfiguration(globalArguments) DokkaGenerator(configuration, globalArguments.logger).generate() } diff --git a/runners/cli/src/test/kotlin/cli/CliTest.kt b/runners/cli/src/test/kotlin/cli/CliTest.kt new file mode 100644 index 0000000000..4ed2e6dc47 --- /dev/null +++ b/runners/cli/src/test/kotlin/cli/CliTest.kt @@ -0,0 +1,29 @@ +package org.jetbrains.dokka + +import junit.framework.Assert.assertTrue +import org.junit.Test +import java.lang.IllegalStateException +import kotlin.test.assertEquals + +class CliIntegrationTest { + + @Test + fun testGlobalArgs() { + val jsonPath = javaClass.getResource("/my-file.json")?.path ?: throw IllegalStateException("No JSON found!") + val globalArguments = GlobalArguments(arrayOf(jsonPath)) + + val configuration = initializeConfiguration(globalArguments) + + configuration.sourceSets.forEach { + assertTrue(it.perPackageOptions.isNotEmpty()) + assertTrue(it.sourceLinks.isNotEmpty()) + assertTrue(it.externalDocumentationLinks.isNotEmpty()) + + assertTrue(it.externalDocumentationLinks.any { it.url.toString() == "https://docs.oracle.com/javase/8/docs/api/" }) + assertEquals(it.sourceLinks.single().localDirectory, "/home/Vadim.Mishenev/dokka/examples/cli/src/main/kotlin") + assertEquals(it.perPackageOptions.single().matchingRegex, "my-custom-regex") + } + + } + +} diff --git a/runners/cli/src/test/resources/my-file.json b/runners/cli/src/test/resources/my-file.json new file mode 100644 index 0000000000..49dda8149d --- /dev/null +++ b/runners/cli/src/test/resources/my-file.json @@ -0,0 +1,51 @@ +{ + "moduleName": "Dokka Example", + "moduleVersion": null, + "outputDir": "$outputPath", + "pluginsClasspath": ["$pluginsClasspath"], + "cacheRoot": null, + "offlineMode": false, + "sourceLinks": [{ + "localDirectory": "/home/Vadim.Mishenev/dokka/examples/cli/src/main/kotlin", + "remoteUrl": "https://github.com/Kotlin/dokka/tree/master/examples/gradle/dokka-gradle-example/src/main/kotlin", + "remoteLineSuffix": "#L" + }], + "externalDocumentationLinks": [{ + "url": "https://docs.oracle.com/javase/8/docs/api/", + "packageListUrl": "https://docs.oracle.com/javase/8/docs/api/package-list" + }], + "perPackageOptions": [{ + "matchingRegex": "my-custom-regex", + "skipDeprecated": "true", + "reportUndocumented": "true", + "includeNonPublic": "true", + "documentedVisibilities": ["PUBLIC", "PRIVATE", "PROTECTED", "INTERNAL", "PACKAGE"] + }], + "sourceSets": [ + { + "displayName": "jvm", + "sourceSetID": { + "scopeId": ":dokkaHtml", + "sourceSetName": "main" + }, + "sourceRoots": [ + "$projectPath" + ], + "dependentSourceSets": [], + "samples": [], + "includes": [], + "includeNonPublic": false, + "reportUndocumented": false, + "skipEmptyPackages": true, + "skipDeprecated": false, + "jdkVersion": 8, + "sourceLinks": [], + "perPackageOptions": [], + "externalDocumentationLinks": [], + "noStdlibLink": false, + "noJdkLink": false, + "suppressedFiles": [], + "analysisPlatform": "jvm" + } + ] +}