From 8e5c63d035ef44a269b8c43430f43f5c8eebfb63 Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Fri, 10 Nov 2023 11:46:54 +0100 Subject: Restructure the project to utilize included builds (#3174) * Refactor and simplify artifact publishing * Update Gradle to 8.4 * Refactor and simplify convention plugins and build scripts Fixes #3132 --------- Co-authored-by: Adam <897017+aSemy@users.noreply.github.com> Co-authored-by: Oleg Yukhnevich --- dokka-subprojects/plugin-versioning/README.md | 332 +++++++++++++++++++++ .../plugin-versioning/api/plugin-versioning.api | 149 +++++++++ .../plugin-versioning/build.gradle.kts | 33 ++ .../DefaultPreviousDocumentationCopyPostAction.kt | 60 ++++ .../versioning/ReplaceVersionCommandConsumer.kt | 54 ++++ .../dokka/versioning/ReplaceVersionsCommand.kt | 29 ++ .../dokka/versioning/VersioningConfiguration.kt | 38 +++ .../jetbrains/dokka/versioning/VersioningPlugin.kt | 70 +++++ .../dokka/versioning/VersioningStorage.kt | 72 +++++ .../dokka/versioning/VersionsNavigationCreator.kt | 91 ++++++ .../jetbrains/dokka/versioning/VersionsOrdering.kt | 26 ++ .../dokka/versioning/htmlPreprocessors.kt | 46 +++ .../org.jetbrains.dokka.plugability.DokkaPlugin | 5 + .../main/resources/dokka/not-found-version.html | 193 ++++++++++++ .../main/resources/dokka/styles/multimodule.css | 55 ++++ .../versioning-plugin-example.png | Bin 0 -> 43684 bytes 16 files changed, 1253 insertions(+) create mode 100644 dokka-subprojects/plugin-versioning/README.md create mode 100644 dokka-subprojects/plugin-versioning/api/plugin-versioning.api create mode 100644 dokka-subprojects/plugin-versioning/build.gradle.kts create mode 100644 dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/DefaultPreviousDocumentationCopyPostAction.kt create mode 100644 dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/ReplaceVersionCommandConsumer.kt create mode 100644 dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/ReplaceVersionsCommand.kt create mode 100644 dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersioningConfiguration.kt create mode 100644 dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersioningPlugin.kt create mode 100644 dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersioningStorage.kt create mode 100644 dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersionsNavigationCreator.kt create mode 100644 dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersionsOrdering.kt create mode 100644 dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/htmlPreprocessors.kt create mode 100644 dokka-subprojects/plugin-versioning/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin create mode 100644 dokka-subprojects/plugin-versioning/src/main/resources/dokka/not-found-version.html create mode 100644 dokka-subprojects/plugin-versioning/src/main/resources/dokka/styles/multimodule.css create mode 100644 dokka-subprojects/plugin-versioning/versioning-plugin-example.png (limited to 'dokka-subprojects/plugin-versioning') diff --git a/dokka-subprojects/plugin-versioning/README.md b/dokka-subprojects/plugin-versioning/README.md new file mode 100644 index 00000000..d501a58f --- /dev/null +++ b/dokka-subprojects/plugin-versioning/README.md @@ -0,0 +1,332 @@ +# Versioning plugin + +The versioning plugin provides the ability to host documentation for multiple versions of your library/application +with seamless switching between them. This, in turn, provides a better experience for your users. + +![Screenshot of documentation version dropdown](versioning-plugin-example.png) + +**Note:** The versioning plugin only works with Dokka's HTML format. + +Visit the [versioning plugin example project](../../examples/gradle/dokka-versioning-multimodule-example) +to see an example of it in action and how it can be configured. + +## Applying the plugin + +You can apply the versioning plugin the same way as other Dokka plugins: + +
+Kotlin + +```kotlin +dependencies { + dokkaHtmlPlugin("org.jetbrains.dokka:versioning-plugin:1.9.10") +} +``` + +**Note:** When documenting multi-project builds, you need to apply the versioning +plugin within subprojects as well as in their parent project. + +
+ +
+Groovy + +```groovy +dependencies { + dokkaHtmlPlugin 'org.jetbrains.dokka:versioning-plugin:1.9.10' +} +``` + +**Note:** When documenting multi-project builds, you need to apply the versioning +plugin within subprojects as well as in their parent project. + +
+ +
+Maven + +```xml + + org.jetbrains.dokka + dokka-maven-plugin + ... + + + + org.jetbrains.dokka + versioning-plugin + 1.9.10 + + + + +``` + +
+ +
+CLI + +You can find the versioning plugin's artifact on +[mvnrepository](https://mvnrepository.com/artifact/org.jetbrains.dokka/versioning-plugin/1.9.10) or by browsing +[maven central repository](https://repo1.maven.org/maven2/org/jetbrains/dokka/versioning-plugin/1.9.10) +directly, and pass it to `pluginsClasspath`. + +Via command line arguments: + +```Bash +java -jar dokka-cli-1.9.10.jar \ + -pluginsClasspath "./dokka-base-1.9.10.jar;...;./versioning-plugin-1.9.10.jar" \ + ... +``` + +Via JSON configuration: + +```json +{ + ... + "pluginsClasspath": [ + "./dokka-base-1.9.10.jar", + "...", + "./versioning-plugin-1.9.10.jar" + ], + ... +} +``` + +
+ +## Configuration + +### Configuration options + +The table below contains all the possible configuration options for the versioning plugin and their purpose. + +| **Option** | **Description** | +|--------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `version` | The version of your application/library that documentation is going to be generated for. This will be the version shown in the dropdown menu. | +| `versionsOrdering` | An optional list of strings that represents the order that versions should appear in the dropdown menu. Must match `version` string exactly. The first item in the list is at the top of the dropdown. | +| `olderVersionsDir` | An optional path to a parent folder that contains other documentation versions. It requires a specific directory structure. For more information, see [Directory structure](#directory-structure). | +| `olderVersions` | An optional list of paths to other documentation versions. It must point to Dokka's outputs directly. This is useful if different versions can't all be in the same directory. | +| `renderVersionsNavigationOnAllPages` | An optional boolean value indicating whether to render the navigation dropdown on all pages. Set to true by default. | + +#### Directory structure + +Note that the directory passed to `olderVersionsDir` needs to follow a specific structure: + +```text +. +└── olderVersionsDir + └── 1.7.10 + ├── + └── 1.7.20 + ├── +... +``` + +### Configuration example + +
+Kotlin + +```kotlin +import org.jetbrains.dokka.versioning.VersioningPlugin +import org.jetbrains.dokka.versioning.VersioningConfiguration + +buildscript { + dependencies { + classpath("org.jetbrains.dokka:versioning-plugin:1.9.10") + } +} + +tasks.dokkaHtml { + pluginConfiguration { + version = "1.5" + versionsOrdering = listOf("1.5", "1.4", "1.3", "1.2", "1.1", "alpha-2", "alpha-1") + olderVersionsDir = file("documentation/version") + olderVersions = listOf(file("documentation/alpha/alpha-2"), file("documentation/alpha/alpha-1")) + renderVersionsNavigationOnAllPages = true + } +} +``` + +Alternatively, you can configure it via JSON: + +```kotlin + val versioningConfiguration = """ + { + "version": "1.5", + "versionsOrdering": ["1.5", "1.4", "1.3", "1.2", "1.1", "alpha-2", "alpha-1"], + "olderVersionsDir": "documentation/version", + "olderVersions": ["documentation/alpha/alpha-2", "documentation/alpha/alpha-1"], + "renderVersionsNavigationOnAllPages": true + } + """ + pluginsMapConfiguration.set( + mapOf( + "org.jetbrains.dokka.versioning.VersioningPlugin" to versioningConfiguration + ) + ) +``` + +
+ +
+Groovy + +```groovy +dokkaHtml { + String versioningConfiguration = """ + { + "version": "1.5", + "versionsOrdering": ["1.5", "1.4", "1.3", "1.2", "1.1", "alpha-2", "alpha-1"], + "olderVersionsDir": "documentation/version", + "olderVersions": ["documentation/alpha/alpha-2", "documentation/alpha/alpha-1"], + "renderVersionsNavigationOnAllPages": true + } + """ + pluginsMapConfiguration.set( + ["org.jetbrains.dokka.versioning.VersioningPlugin": versioningConfiguration] + ) +} +``` + +
+ +
+Maven + +```xml + + org.jetbrains.dokka + dokka-maven-plugin + ... + + + + 1.5 + + 1.5 + 1.4 + 1.3 + 1.2 + 1.1 + alpha-2 + alpha-1 + + ${project.basedir}/documentation/version + + ${project.basedir}/documentation/alpha/alpha-2 + ${project.basedir}/documentation/alpha/alpha-1 + + true + + + + +``` + +
+ +
+CLI + +```Bash +java -jar dokka-cli-1.9.10.jar \ + ... + -pluginsConfiguration "org.jetbrains.dokka.versioning.VersioningPlugin={\"version\": \"1.5\", \"versionsOrdering\": [\"1.5\", \"1.4\", \"1.3\", \"1.2\", \"1.1\", \"alpha-2\", \"alpha-1\"], \"olderVersionsDir\": \"documentation/version\", \"olderVersions\": [\"documentation/alpha/alpha-2\", \"documentation/alpha/alpha-1\"], \"renderVersionsNavigationOnAllPages\": true}" + +``` + +Alternatively, via JSON configuration: +```json +{ + "moduleName": "Dokka Example", + ... + "pluginsConfiguration": [ + { + "fqPluginName": "org.jetbrains.dokka.versioning.VersioningPlugin", + "serializationFormat": "JSON", + "values": "{\"version\": \"1.5\", \"versionsOrdering\": [\"1.5\", \"1.4\", \"1.3\", \"1.2\", \"1.1\", \"alpha-2\", \"alpha-1\"], \"olderVersionsDir\": \"documentation/version\", \"olderVersions\": [\"documentation/alpha/alpha-2\", \"documentation/alpha/alpha-1\"], \"renderVersionsNavigationOnAllPages\": true}" + } + ] +} +``` + +
+ +## Generating versioned documentation + +With the versioning plugin applied and configured, no other steps are needed. Documentation can be built in the usual way. + +Among other things, the versioning plugin adds a `version.json` file to the output folder. This file is used by the +plugin to match versions and generate version navigation. If your previously generated documentation does not have that +file, you will need to re-generate documentation for such versions. Just adding the file will not work. + +The versioning plugin also bundles all other documentation versions that have been passed through `olderVersionsDir` +and `olderVersions` configuration options by putting them inside the `older` directory. + +## Usage example + +There is no single correct way to configure the plugin, it can be tailored to your needs. However, +it can be a bit overwhelming when starting out. Below you will find one of the ways it can be configured so that you +can begin publishing versioned documentation straight away. + +The main idea behind it is the following: + +1. One directory contains all versions of your documentation. For example, `documentation/version/{doc_version}`. + This is your archive which is needed for future builds. +2. The output directory of all new builds is set to that directory as well, under `documentation/version/{new_version}`. +3. When new builds are executed, the plugin looks for previous versions of documentation in the archive directory. +4. Once new documentation has been generated, it needs to be **copied** to somewhere accessible by the user. + For example, GitHub pages or nginx static directories. It needs to be **copied**, not moved because Dokka will still + need this version for future builds, otherwise there will be a gap in the archive. +5. Once it has been safely copied, you can remove the `older` directory from the newly generated and archived version. + This helps reduce the overhead of each version bundling all previous versions, as these files are effectively duplicates. + +```kotlin +import org.jetbrains.dokka.versioning.VersioningPlugin +import org.jetbrains.dokka.versioning.VersioningConfiguration + +buildscript { + dependencies { + classpath("org.jetbrains.dokka:versioning-plugin:1.9.10") + } +} + +dependencies { + dokkaPlugin("org.jetbrains.dokka:versioning-plugin:1.9.10") +} + +tasks.dokkaHtml { + // This can be any persistent folder where + // you store documentation by version + val docVersionsDir = projectDir.resolve("documentation/version") + + // The version for which you are currently generating docs + val currentVersion = "1.3" + + // Set the output to a folder with all other versions + // as you'll need the current version for future builds + val currentDocsDir = docVersionsDir.resolve(currentVersion) + outputDirectory.set(currentDocsDir) + + pluginConfiguration { + olderVersionsDir = docVersionsDir + version = currentVersion + } + + doLast { + // This folder contains the latest documentation with all + // previous versions included, so it's ready to be published. + // Make sure it's copied and not moved - you'll still need this + // version for future builds + currentDocsDir.copyTo(file("/my/hosting")) + + // Only once current documentation has been safely moved, + // remove previous versions bundled in it. They will not + // be needed in future builds, it's just overhead. + currentDocsDir.resolve("older").deleteRecursively() + } +} +``` diff --git a/dokka-subprojects/plugin-versioning/api/plugin-versioning.api b/dokka-subprojects/plugin-versioning/api/plugin-versioning.api new file mode 100644 index 00000000..6ba2c822 --- /dev/null +++ b/dokka-subprojects/plugin-versioning/api/plugin-versioning.api @@ -0,0 +1,149 @@ +public final class org/jetbrains/dokka/versioning/ByConfigurationVersionOrdering : org/jetbrains/dokka/versioning/VersionsOrdering { + public fun (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public final fun getDokkaContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; + public fun order (Ljava/util/List;)Ljava/util/List; +} + +public final class org/jetbrains/dokka/versioning/CurrentVersion { + public fun (Ljava/lang/String;Ljava/io/File;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/io/File; + public final fun copy (Ljava/lang/String;Ljava/io/File;)Lorg/jetbrains/dokka/versioning/CurrentVersion; + public static synthetic fun copy$default (Lorg/jetbrains/dokka/versioning/CurrentVersion;Ljava/lang/String;Ljava/io/File;ILjava/lang/Object;)Lorg/jetbrains/dokka/versioning/CurrentVersion; + public fun equals (Ljava/lang/Object;)Z + public final fun getDir ()Ljava/io/File; + public final fun getName ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/jetbrains/dokka/versioning/DefaultPreviousDocumentationCopyPostAction : org/jetbrains/dokka/renderers/PostAction { + public fun (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public synthetic fun invoke ()Ljava/lang/Object; + public fun invoke ()V +} + +public final class org/jetbrains/dokka/versioning/DefaultVersioningStorage : org/jetbrains/dokka/versioning/VersioningStorage { + public fun (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public fun createVersionFile ()V + public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; + public fun getCurrentVersion ()Lorg/jetbrains/dokka/versioning/CurrentVersion; + public fun getPreviousVersions ()Ljava/util/Map; +} + +public final class org/jetbrains/dokka/versioning/HtmlVersionsNavigationCreator : org/jetbrains/dokka/versioning/VersionsNavigationCreator { + public fun (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public fun invoke (Ljava/io/File;)Ljava/lang/String; +} + +public final class org/jetbrains/dokka/versioning/MultiModuleStylesInstaller : org/jetbrains/dokka/transformers/pages/PageTransformer { + public fun (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public fun invoke (Lorg/jetbrains/dokka/pages/RootPageNode;)Lorg/jetbrains/dokka/pages/RootPageNode; +} + +public final class org/jetbrains/dokka/versioning/NotFoundPageInstaller : org/jetbrains/dokka/transformers/pages/PageTransformer { + public fun (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public fun invoke (Lorg/jetbrains/dokka/pages/RootPageNode;)Lorg/jetbrains/dokka/pages/RootPageNode; +} + +public final class org/jetbrains/dokka/versioning/ReplaceVersionCommandConsumer : org/jetbrains/dokka/base/templating/ImmediateHtmlCommandConsumer { + public fun (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public fun canProcess (Lorg/jetbrains/dokka/base/templating/Command;)Z + public fun processCommand (Lorg/jetbrains/dokka/base/templating/Command;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/dokka/base/renderers/html/command/consumers/ImmediateResolutionTagConsumer;)V + public fun processCommandAndFinalize (Lorg/jetbrains/dokka/base/templating/Command;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/dokka/base/renderers/html/command/consumers/ImmediateResolutionTagConsumer;)Ljava/lang/Object; +} + +public final class org/jetbrains/dokka/versioning/ReplaceVersionCommandHandler : org/jetbrains/dokka/templates/CommandHandler { + public fun (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public fun canHandle (Lorg/jetbrains/dokka/base/templating/Command;)Z + public fun finish (Ljava/io/File;)V + public final fun getVersionsNavigationCreator ()Lorg/jetbrains/dokka/versioning/VersionsNavigationCreator; + public fun handleCommand (Lorg/jsoup/nodes/Element;Lorg/jetbrains/dokka/base/templating/Command;Ljava/io/File;Ljava/io/File;)V + public fun handleCommandAsComment (Lorg/jetbrains/dokka/base/templating/Command;Ljava/util/List;Ljava/io/File;Ljava/io/File;)V + public fun handleCommandAsTag (Lorg/jetbrains/dokka/base/templating/Command;Lorg/jsoup/nodes/Element;Ljava/io/File;Ljava/io/File;)V +} + +public final class org/jetbrains/dokka/versioning/SemVerVersionOrdering : org/jetbrains/dokka/versioning/VersionsOrdering { + public fun ()V + public fun order (Ljava/util/List;)Ljava/util/List; +} + +public final class org/jetbrains/dokka/versioning/VersionDirs { + public fun (Ljava/io/File;Ljava/io/File;)V + public final fun component1 ()Ljava/io/File; + public final fun component2 ()Ljava/io/File; + public final fun copy (Ljava/io/File;Ljava/io/File;)Lorg/jetbrains/dokka/versioning/VersionDirs; + public static synthetic fun copy$default (Lorg/jetbrains/dokka/versioning/VersionDirs;Ljava/io/File;Ljava/io/File;ILjava/lang/Object;)Lorg/jetbrains/dokka/versioning/VersionDirs; + public fun equals (Ljava/lang/Object;)Z + public final fun getDst ()Ljava/io/File; + public final fun getSrc ()Ljava/io/File; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/jetbrains/dokka/versioning/VersioningConfiguration : org/jetbrains/dokka/plugability/ConfigurableBlock { + public static final field Companion Lorg/jetbrains/dokka/versioning/VersioningConfiguration$Companion; + public static final field OLDER_VERSIONS_DIR Ljava/lang/String; + public static final field VERSIONS_FILE Ljava/lang/String; + public fun ()V + public fun (Ljava/io/File;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Boolean;)V + public synthetic fun (Ljava/io/File;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Boolean;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/io/File; + public final fun component2 ()Ljava/util/List; + public final fun component3 ()Ljava/util/List; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/lang/Boolean; + public final fun copy (Ljava/io/File;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Boolean;)Lorg/jetbrains/dokka/versioning/VersioningConfiguration; + public static synthetic fun copy$default (Lorg/jetbrains/dokka/versioning/VersioningConfiguration;Ljava/io/File;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Boolean;ILjava/lang/Object;)Lorg/jetbrains/dokka/versioning/VersioningConfiguration; + public fun equals (Ljava/lang/Object;)Z + public final fun getOlderVersions ()Ljava/util/List; + public final fun getOlderVersionsDir ()Ljava/io/File; + public final fun getRenderVersionsNavigationOnAllPages ()Ljava/lang/Boolean; + public final fun getVersion ()Ljava/lang/String; + public final fun getVersionsOrdering ()Ljava/util/List; + public fun hashCode ()I + public final fun setOlderVersions (Ljava/util/List;)V + public final fun setOlderVersionsDir (Ljava/io/File;)V + public final fun setRenderVersionsNavigationOnAllPages (Ljava/lang/Boolean;)V + public final fun setVersion (Ljava/lang/String;)V + public final fun setVersionsOrdering (Ljava/util/List;)V + public fun toString ()Ljava/lang/String; +} + +public final class org/jetbrains/dokka/versioning/VersioningConfiguration$Companion { + public final fun getDefaultOlderVersions ()Ljava/util/List; + public final fun getDefaultOlderVersionsDir ()Ljava/io/File; + public final fun getDefaultRenderVersionsNavigationOnAllPages ()Z + public final fun getDefaultVersion ()Ljava/lang/String; + public final fun getDefaultVersionsOrdering ()Ljava/util/List; +} + +public final class org/jetbrains/dokka/versioning/VersioningPlugin : org/jetbrains/dokka/plugability/DokkaPlugin { + public fun ()V + public final fun getCssStyleInstaller ()Lorg/jetbrains/dokka/plugability/Extension; + public final fun getDefaultVersioningNavigationCreator ()Lorg/jetbrains/dokka/plugability/Extension; + public final fun getDefaultVersioningStorage ()Lorg/jetbrains/dokka/plugability/Extension; + public final fun getNotFoundPageInstaller ()Lorg/jetbrains/dokka/plugability/Extension; + public final fun getPreviousDocumentationCopyPostAction ()Lorg/jetbrains/dokka/plugability/Extension; + public final fun getReplaceVersionCommandHandler ()Lorg/jetbrains/dokka/plugability/Extension; + public final fun getResolveLinkConsumer ()Lorg/jetbrains/dokka/plugability/Extension; + public final fun getVersioningStorage ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getVersionsDefaultOrdering ()Lorg/jetbrains/dokka/plugability/Extension; + public final fun getVersionsNavigationCreator ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getVersionsOrdering ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; +} + +public abstract interface class org/jetbrains/dokka/versioning/VersioningStorage { + public abstract fun createVersionFile ()V + public abstract fun getCurrentVersion ()Lorg/jetbrains/dokka/versioning/CurrentVersion; + public abstract fun getPreviousVersions ()Ljava/util/Map; +} + +public abstract interface class org/jetbrains/dokka/versioning/VersionsNavigationCreator { + public abstract fun invoke (Ljava/io/File;)Ljava/lang/String; +} + +public abstract interface class org/jetbrains/dokka/versioning/VersionsOrdering { + public abstract fun order (Ljava/util/List;)Ljava/util/List; +} + diff --git a/dokka-subprojects/plugin-versioning/build.gradle.kts b/dokka-subprojects/plugin-versioning/build.gradle.kts new file mode 100644 index 00000000..941661bd --- /dev/null +++ b/dokka-subprojects/plugin-versioning/build.gradle.kts @@ -0,0 +1,33 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +import dokkabuild.overridePublicationArtifactId + +plugins { + id("dokkabuild.kotlin-jvm") + id("dokkabuild.publish-jvm") +} + +overridePublicationArtifactId("versioning-plugin") + +dependencies { + compileOnly(projects.dokkaSubprojects.dokkaCore) + + implementation(projects.dokkaSubprojects.pluginBase) + implementation(projects.dokkaSubprojects.pluginTemplating) + + implementation(kotlin("reflect")) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.html) + implementation(libs.apacheMaven.artifact) + implementation(libs.jackson.kotlin) + constraints { + implementation(libs.jackson.databind) { + because("CVE-2022-42003") + } + } + + testImplementation(kotlin("test")) + testImplementation(projects.dokkaSubprojects.coreTestApi) +} diff --git a/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/DefaultPreviousDocumentationCopyPostAction.kt b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/DefaultPreviousDocumentationCopyPostAction.kt new file mode 100644 index 00000000..7e03f59c --- /dev/null +++ b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/DefaultPreviousDocumentationCopyPostAction.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.versioning + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.query +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.renderers.PostAction +import org.jetbrains.dokka.templates.TemplateProcessingStrategy +import org.jetbrains.dokka.templates.TemplatingPlugin +import java.io.File + +public class DefaultPreviousDocumentationCopyPostAction( + private val context: DokkaContext +) : PostAction { + private val versioningStorage by lazy { context.plugin().querySingle { versioningStorage } } + private val processingStrategies: List = + context.plugin().query { templateProcessingStrategy } + + override fun invoke() { + versioningStorage.createVersionFile() + versioningStorage.previousVersions.forEach { (_, dirs) -> copyVersion(dirs.src, dirs.dst) } + } + + private fun copyVersion(versionRoot: File, targetParent: File) { + targetParent.apply { mkdirs() } + val ignoreDir = versionRoot.resolve(VersioningConfiguration.OLDER_VERSIONS_DIR) + runBlocking(Dispatchers.Default) { + coroutineScope { + versionRoot.listFiles().orEmpty() + .filter { it.absolutePath != ignoreDir.absolutePath } + .forEach { versionRootContent -> + launch { + processRecursively(versionRootContent, targetParent) + } + } + } + } + } + + private fun processRecursively(versionRootContent: File, targetParent: File) { + if (versionRootContent.isDirectory) { + val target = targetParent.resolve(versionRootContent.name).also { it.mkdir() } + versionRootContent.listFiles()?.forEach { + processRecursively(it, target) + } + } else if (versionRootContent.extension == "html") processingStrategies.first { + it.process(versionRootContent, targetParent.resolve(versionRootContent.name), null) + } else { + versionRootContent.copyTo(targetParent.resolve(versionRootContent.name), overwrite = true) + } + } +} diff --git a/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/ReplaceVersionCommandConsumer.kt b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/ReplaceVersionCommandConsumer.kt new file mode 100644 index 00000000..b31afb9a --- /dev/null +++ b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/ReplaceVersionCommandConsumer.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.versioning + +import kotlinx.html.unsafe +import kotlinx.html.visit +import kotlinx.html.visitAndFinalize +import org.jetbrains.dokka.base.renderers.html.TemplateBlock +import org.jetbrains.dokka.base.renderers.html.command.consumers.ImmediateResolutionTagConsumer +import org.jetbrains.dokka.base.renderers.html.templateCommandFor +import org.jetbrains.dokka.base.templating.Command +import org.jetbrains.dokka.base.templating.ImmediateHtmlCommandConsumer +import org.jetbrains.dokka.base.templating.ReplaceVersionsCommand +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle + +public class ReplaceVersionCommandConsumer(context: DokkaContext) : ImmediateHtmlCommandConsumer { + + private val versionsNavigationCreator = + context.plugin().querySingle { versionsNavigationCreator } + private val versioningStorage = + context.plugin().querySingle { versioningStorage } + + override fun canProcess(command: Command): Boolean = command is ReplaceVersionsCommand + + override fun processCommand( + command: Command, + block: TemplateBlock, + tagConsumer: ImmediateResolutionTagConsumer + ) { + command as ReplaceVersionsCommand + templateCommandFor(command, tagConsumer).visit { + unsafe { + +versionsNavigationCreator(versioningStorage.currentVersion.dir.resolve(command.location)) + } + } + } + + override fun processCommandAndFinalize( + command: Command, + block: TemplateBlock, + tagConsumer: ImmediateResolutionTagConsumer + ): R { + command as ReplaceVersionsCommand + return templateCommandFor(command, tagConsumer).visitAndFinalize(tagConsumer) { + unsafe { + +versionsNavigationCreator(versioningStorage.currentVersion.dir.resolve(command.location)) + } + } + } +} diff --git a/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/ReplaceVersionsCommand.kt b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/ReplaceVersionsCommand.kt new file mode 100644 index 00000000..c9bc57b2 --- /dev/null +++ b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/ReplaceVersionsCommand.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.versioning + + +import org.jetbrains.dokka.base.templating.Command +import org.jetbrains.dokka.base.templating.ReplaceVersionsCommand +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.templates.CommandHandler +import org.jsoup.nodes.Element +import java.io.File + +public class ReplaceVersionCommandHandler(context: DokkaContext) : CommandHandler { + + public val versionsNavigationCreator: VersionsNavigationCreator by lazy { + context.plugin().querySingle { versionsNavigationCreator } + } + + override fun canHandle(command: Command): Boolean = command is ReplaceVersionsCommand + + override fun handleCommandAsTag(command: Command, body: Element, input: File, output: File) { + body.empty() + body.append(versionsNavigationCreator(output)) + } +} diff --git a/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersioningConfiguration.kt b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersioningConfiguration.kt new file mode 100644 index 00000000..91b1117d --- /dev/null +++ b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersioningConfiguration.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.versioning + +import org.jetbrains.dokka.plugability.ConfigurableBlock +import org.jetbrains.dokka.plugability.DokkaContext +import java.io.File + +public data class VersioningConfiguration( + var olderVersionsDir: File? = defaultOlderVersionsDir, + var olderVersions: List? = defaultOlderVersions, + var versionsOrdering: List? = defaultVersionsOrdering, + var version: String? = defaultVersion, + var renderVersionsNavigationOnAllPages: Boolean? = defaultRenderVersionsNavigationOnAllPages +) : ConfigurableBlock { + internal fun versionFromConfigurationOrModule(dokkaContext: DokkaContext): String = + version ?: dokkaContext.configuration.moduleVersion ?: "1.0" + + internal fun allOlderVersions(): List { + if (olderVersionsDir != null) + assert(olderVersionsDir!!.isDirectory) { "Supplied previous version $olderVersionsDir is not a directory!" } + + return olderVersionsDir?.listFiles()?.toList().orEmpty() + olderVersions.orEmpty() + } + + public companion object { + public val defaultOlderVersionsDir: File? = null + public val defaultOlderVersions: List? = null + public val defaultVersionsOrdering: List? = null + public val defaultVersion: String? = null + public val defaultRenderVersionsNavigationOnAllPages: Boolean = true + + public const val OLDER_VERSIONS_DIR: String = "older" + public const val VERSIONS_FILE: String = "version.json" + } +} diff --git a/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersioningPlugin.kt b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersioningPlugin.kt new file mode 100644 index 00000000..2e1fde8d --- /dev/null +++ b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersioningPlugin.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.versioning + +import org.jetbrains.dokka.CoreExtensions.postActions +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.templating.ImmediateHtmlCommandConsumer +import org.jetbrains.dokka.plugability.* +import org.jetbrains.dokka.renderers.PostAction +import org.jetbrains.dokka.templates.CommandHandler +import org.jetbrains.dokka.templates.TemplatingPlugin +import org.jetbrains.dokka.transformers.pages.PageTransformer + +public class VersioningPlugin : DokkaPlugin() { + + public val versioningStorage: ExtensionPoint by extensionPoint() + public val versionsNavigationCreator: ExtensionPoint by extensionPoint() + public val versionsOrdering: ExtensionPoint by extensionPoint() + + private val dokkaBase by lazy { plugin() } + private val templatingPlugin by lazy { plugin() } + + public val defaultVersioningStorage: Extension by extending { + versioningStorage providing ::DefaultVersioningStorage + } + + public val defaultVersioningNavigationCreator: Extension by extending { + versionsNavigationCreator providing ::HtmlVersionsNavigationCreator + } + + public val replaceVersionCommandHandler: Extension by extending { + templatingPlugin.directiveBasedCommandHandlers providing ::ReplaceVersionCommandHandler override templatingPlugin.replaceVersionCommandHandler + } + + public val resolveLinkConsumer: Extension by extending { + dokkaBase.immediateHtmlCommandConsumer providing ::ReplaceVersionCommandConsumer override dokkaBase.replaceVersionConsumer + } + + public val cssStyleInstaller: Extension by extending { + dokkaBase.htmlPreprocessors providing ::MultiModuleStylesInstaller order { + after(dokkaBase.assetsInstaller) + before(dokkaBase.customResourceInstaller) + } + } + + public val notFoundPageInstaller: Extension by extending { + dokkaBase.htmlPreprocessors providing ::NotFoundPageInstaller order { + after(dokkaBase.assetsInstaller) + before(dokkaBase.customResourceInstaller) + } applyIf { !delayTemplateSubstitution } + } + + public val versionsDefaultOrdering: Extension by extending { + versionsOrdering providing { ctx -> + configuration(ctx)?.versionsOrdering?.let { + ByConfigurationVersionOrdering(ctx) + } ?: SemVerVersionOrdering() + } + } + + public val previousDocumentationCopyPostAction: Extension by extending { + postActions providing ::DefaultPreviousDocumentationCopyPostAction applyIf { !delayTemplateSubstitution } + } + + @OptIn(DokkaPluginApiPreview::class) + override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = + PluginApiPreviewAcknowledgement +} diff --git a/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersioningStorage.kt b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersioningStorage.kt new file mode 100644 index 00000000..7c9d1da0 --- /dev/null +++ b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersioningStorage.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.versioning + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.configuration +import java.io.File + +public data class VersionDirs(val src: File, val dst: File) +public data class CurrentVersion(val name: String, val dir: File) + +public interface VersioningStorage { + public val previousVersions: Map + public val currentVersion: CurrentVersion + + public fun createVersionFile() +} + +public typealias VersionId = String + +public class DefaultVersioningStorage( + public val context: DokkaContext +) : VersioningStorage { + + private val mapper = ObjectMapper() + private val configuration = configuration(context) + + override val previousVersions: Map by lazy { + configuration?.let { versionsConfiguration -> + getPreviousVersions(versionsConfiguration.allOlderVersions(), context.configuration.outputDir) + } ?: emptyMap() + } + + override val currentVersion: CurrentVersion by lazy { + configuration?.let { versionsConfiguration -> + CurrentVersion(versionsConfiguration.versionFromConfigurationOrModule(context), + context.configuration.outputDir) + }?: CurrentVersion(context.configuration.moduleVersion.orEmpty(), context.configuration.outputDir) + } + + override fun createVersionFile() { + mapper.writeValue( + currentVersion.dir.resolve(VersioningConfiguration.VERSIONS_FILE), + Version(currentVersion.name) + ) + } + + private fun getPreviousVersions(olderVersions: List, output: File): Map = + versionsFrom(olderVersions).associate { (key, srcDir) -> + key to VersionDirs(srcDir, output.resolve(VersioningConfiguration.OLDER_VERSIONS_DIR).resolve(key)) + } + + private fun versionsFrom(olderVersions: List) = + olderVersions.mapNotNull { versionDir -> + versionDir.listFiles { _, name -> name == VersioningConfiguration.VERSIONS_FILE }?.firstOrNull() + ?.let { file -> + val versionsContent = mapper.readValue(file) + Pair(versionsContent.version, versionDir) + }.also { + if (it == null) context.logger.warn("Failed to find versions file named ${VersioningConfiguration.VERSIONS_FILE} in $versionDir") + } + } + + private data class Version( + @JsonProperty("version") val version: String, + ) +} diff --git a/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersionsNavigationCreator.kt b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersionsNavigationCreator.kt new file mode 100644 index 00000000..59ce93e2 --- /dev/null +++ b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersionsNavigationCreator.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.versioning + +import kotlinx.html.a +import kotlinx.html.div +import kotlinx.html.stream.appendHTML +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.configuration +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.utilities.urlEncoded +import java.io.File + +public fun interface VersionsNavigationCreator { + public operator fun invoke(output: File): String +} + +public class HtmlVersionsNavigationCreator( + private val context: DokkaContext +) : VersionsNavigationCreator { + + private val versioningStorage by lazy { context.plugin().querySingle { versioningStorage } } + + private val versionsOrdering by lazy { context.plugin().querySingle { versionsOrdering } } + + private val isOnlyOnRootPage = + configuration(context)?.renderVersionsNavigationOnAllPages == false + + private val versions: Map by lazy { + versioningStorage.previousVersions.map { (k, v) -> k to v.dst }.toMap() + + (versioningStorage.currentVersion.name to versioningStorage.currentVersion.dir) + } + + override fun invoke(output: File): String { + if (versions.size == 1) { + return versioningStorage.currentVersion.name + } + val position = output.takeIf { it.isDirectory } ?: output.parentFile + if (isOnlyOnRootPage) { + getActiveVersion(position)?.takeIf { + it.value == versioningStorage.currentVersion.dir + && it.value != position + }?.also { return@invoke it.key } + } + return versions + .let { versions -> versionsOrdering.order(versions.keys.toList()).map { it to versions[it] } } + .takeIf { it.isNotEmpty() } + ?.let { orderedVersions -> + StringBuilder().appendHTML().div(classes = "versions-dropdown") { + val activeVersion = getActiveVersion(position) + val relativePosition: String = activeVersion?.value?.let { output.toRelativeString(it) } ?: "index.html" + div(classes = "versions-dropdown-button") { + activeVersion?.key?.let { text(it) } + } + div(classes = "versions-dropdown-data") { + orderedVersions.forEach { (version, path) -> + if (version == activeVersion?.key) { + a(href = output.name) { text(version) } + } else { + val isExistsFile = + if (version == versioningStorage.currentVersion.name) + path?.resolve(relativePosition)?.exists() == true + else + versioningStorage.previousVersions[version]?.src?.resolve(relativePosition) + ?.exists() == true + + val absolutePath = + if (isExistsFile) + path?.resolve(relativePosition) + else + versioningStorage.currentVersion.dir.resolve("not-found-version.html") + + a(href = absolutePath?.toRelativeString(position) + + if (!isExistsFile) "?v=" + version.urlEncoded() else "") { + text(version) + } + } + } + } + }.toString() + }.orEmpty() + } + + private fun getActiveVersion(position: File) = + versions.minByOrNull { (_, versionLocation) -> + versionLocation.let { position.toRelativeString(it).length } + } +} diff --git a/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersionsOrdering.kt b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersionsOrdering.kt new file mode 100644 index 00000000..3d1fbe3d --- /dev/null +++ b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/VersionsOrdering.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.versioning + +import org.apache.maven.artifact.versioning.ComparableVersion +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.configuration + +public fun interface VersionsOrdering { + public fun order(records: List): List +} + +public class ByConfigurationVersionOrdering( + public val dokkaContext: DokkaContext +) : VersionsOrdering { + override fun order(records: List): List = + configuration(dokkaContext)?.versionsOrdering + ?: throw IllegalStateException("Attempted to use a configuration ordering without providing configuration") +} + +public class SemVerVersionOrdering : VersionsOrdering { + override fun order(records: List): List = + records.map { it to ComparableVersion(it) }.sortedByDescending { it.second }.map { it.first } +} diff --git a/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/htmlPreprocessors.kt b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/htmlPreprocessors.kt new file mode 100644 index 00000000..9bdaf7d5 --- /dev/null +++ b/dokka-subprojects/plugin-versioning/src/main/kotlin/org/jetbrains/dokka/versioning/htmlPreprocessors.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.versioning + +import org.jetbrains.dokka.pages.RendererSpecificResourcePage +import org.jetbrains.dokka.pages.RenderingStrategy +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.pages.PageTransformer + +public class MultiModuleStylesInstaller( + private val dokkaContext: DokkaContext +) : PageTransformer { + private val stylesPages = listOf( + "styles/multimodule.css", + ) + + override fun invoke(input: RootPageNode): RootPageNode = + input.let { root -> + if (dokkaContext.configuration.delayTemplateSubstitution) root + else root.modified(children = input.children + stylesPages.toRenderSpecificResourcePage()) + }.transformContentPagesTree { + it.modified( + embeddedResources = it.embeddedResources + stylesPages + ) + } +} + +public class NotFoundPageInstaller( + private val dokkaContext: DokkaContext +) : PageTransformer { + private val notFoundPage = listOf( + "not-found-version.html", + ) + + override fun invoke(input: RootPageNode): RootPageNode = + input.let { root -> + if (dokkaContext.configuration.delayTemplateSubstitution) root + else root.modified(children = input.children + notFoundPage.toRenderSpecificResourcePage()) + } +} + +private fun List.toRenderSpecificResourcePage(): List = + map { RendererSpecificResourcePage(it, emptyList(), RenderingStrategy.Copy("/dokka/$it")) } diff --git a/dokka-subprojects/plugin-versioning/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/dokka-subprojects/plugin-versioning/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..2afa663b --- /dev/null +++ b/dokka-subprojects/plugin-versioning/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1,5 @@ +# +# Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. +# + +org.jetbrains.dokka.versioning.VersioningPlugin diff --git a/dokka-subprojects/plugin-versioning/src/main/resources/dokka/not-found-version.html b/dokka-subprojects/plugin-versioning/src/main/resources/dokka/not-found-version.html new file mode 100644 index 00000000..36cf343d --- /dev/null +++ b/dokka-subprojects/plugin-versioning/src/main/resources/dokka/not-found-version.html @@ -0,0 +1,193 @@ + + + + + + + Unavailable page + + + + + + + + + + + NOT + FOUND + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+

uh-oh!

+
You are requesting a page that not + available in documentation version +
+
+
+
+ + diff --git a/dokka-subprojects/plugin-versioning/src/main/resources/dokka/styles/multimodule.css b/dokka-subprojects/plugin-versioning/src/main/resources/dokka/styles/multimodule.css new file mode 100644 index 00000000..91798c1d --- /dev/null +++ b/dokka-subprojects/plugin-versioning/src/main/resources/dokka/styles/multimodule.css @@ -0,0 +1,55 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +.versions-dropdown { + position: relative; +} + +.versions-dropdown-button { + display: flex; + border: none; + cursor: pointer; + padding: 5px; +} + +.versions-dropdown-button::after { + content: ''; + -webkit-mask: url("../images/arrow_down.svg") no-repeat 50% 50%; + mask: url("../images/arrow_down.svg") no-repeat 50% 50%; + mask-size: auto; + -webkit-mask-size: cover; + mask-size: cover; + background-color: #fff; + display: inline-block; + transform: rotate(90deg); + width: 24px; + height: 16px; +} + +.versions-dropdown-data { + display: none; + position: absolute; + background-color: #27282c; + border: 1px solid hsla(0, 0%, 100%, .6); + box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2); + z-index: 1; + overflow-y: auto; + max-height: 200px; + min-width: 50px; +} + +.versions-dropdown-data > a { + display: block; + padding: 5px; + color: #fff; + text-decoration: none; +} + +.versions-dropdown-data > a:hover { + background-color: hsla(0,0%,100%,.1) +} + +.versions-dropdown:hover .versions-dropdown-data { + display: block; +} diff --git a/dokka-subprojects/plugin-versioning/versioning-plugin-example.png b/dokka-subprojects/plugin-versioning/versioning-plugin-example.png new file mode 100644 index 00000000..cd02c558 Binary files /dev/null and b/dokka-subprojects/plugin-versioning/versioning-plugin-example.png differ -- cgit