diff --git a/build.gradle.kts b/build.gradle.kts index 3800159..1e7aa94 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -64,7 +64,11 @@ val dokka_version: String by project dependencies { compileOnly("org.jetbrains.dokka:dokka-core:$dokka_version") + + // Expose dependency to dokka in .pom file apiElements("org.jetbrains.dokka:dokka-core:$dokka_version") + apiElements("org.jetbrains.dokka:dokka-base:$dokka_version") + implementation("org.jetbrains.dokka:dokka-base:$dokka_version") implementation("com.vladsch.flexmark:flexmark-all:0.42.12") implementation("nl.big-o:liqp:0.6.7") @@ -119,5 +123,6 @@ publishing { // Configure dokka tasks.dokkaHtml { - pluginsConfiguration.put("ExternalDocsTooKey", "documentation") + // TODO (#37): use pluginConfiguration + pluginsMapConfiguration.put("ExternalDocsTooKey", "documentation") } diff --git a/gradle.properties b/gradle.properties index 42a1667..cb69c4a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ kotlin.code.style=official -kotlin_version=1.4.0 -dokka_version=1.4.0 +kotlin_version=1.4.10 +dokka_version=1.4.10.2 language_version=1.4 diff --git a/src/main/kotlin/com/virtuslab/dokka/site/StaticSiteContext.kt b/src/main/kotlin/com/virtuslab/dokka/site/StaticSiteContext.kt new file mode 100644 index 0000000..b6603d2 --- /dev/null +++ b/src/main/kotlin/com/virtuslab/dokka/site/StaticSiteContext.kt @@ -0,0 +1,143 @@ +package com.virtuslab.dokka.site + +import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.doc.DocTag +import org.jetbrains.dokka.model.doc.Text +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.toDisplaySourceSet +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.DCI +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.plugability.DokkaContext +import java.io.File + +class StaticSiteContext(val root: File, cxt: DokkaContext){ + val docsFile = File(root, "docs") + + fun indexPage(): BaseStaticSiteProcessor.StaticPageNode? { + val files = listOf(File(root, "index.html"), File(root, "index.md")).filter { it.exists() } + if (files.size > 1) println("ERROR: Multiple root index pages found: ${files.map { it.absolutePath }}") // TODO (#14): provide proper error handling + return loadFiles(files).firstOrNull() + } + + private val mySourceSet = cxt.configuration.sourceSets.toSet() + private val myDisplaySourceSet = mySourceSet.map { it.toDisplaySourceSet() }.toSet() + + private val layouts: Map by lazy { + val layoutRoot = File(root, "_layouts") + val dirs: Array = layoutRoot.listFiles() ?: emptyArray() + dirs.map { loadTemplateFile(it) }.map { it.name() to it }.toMap() + } + + + private fun isValidTemplate(file: File): Boolean = + (file.isDirectory && !file.name.startsWith("_")) || + file.name.endsWith(".md") || + file.name.endsWith(".html") + + + private fun loadTemplate(from: File): BaseStaticSiteProcessor.LoadedTemplate? = + if (!isValidTemplate(from)) null else try { + val (indexes, children) = (from.listFiles()?.mapNotNull { loadTemplate(it) } + ?: emptyList()).partition { it.isIndexPage() } + if (indexes.size > 1) + println("ERROR: Multiple index pages for $from found in ${indexes.map { it.file }}") // TODO (#14): provide proper error handling + + fun loadIndexPage(): TemplateFile { + val indexFiles = from.listFiles { file -> file.name == "index.md" || file.name == "index.html" } + return when (indexFiles.size) { + 0 -> emptyTemplate(from) + 1 -> loadTemplateFile(indexFiles.first()).copy(file = from) + else -> throw java.lang.RuntimeException("ERROR: Multiple index pages found under ${from.path}") + } + } + + val templateFile = if (from.isDirectory) loadIndexPage() else loadTemplateFile(from) + + BaseStaticSiteProcessor.LoadedTemplate(templateFile, children, from) + + } catch (e: RuntimeException) { + e.printStackTrace() // TODO (#14): provide proper error handling + null + } + + private fun parseMarkdown(page: PreResolvedPage, dri: DRI, allDRIs: Map): ContentNode { + val nodes = if (page.hasMarkdown) { + val parser = ExtendableMarkdownParser(page.code) { link -> + val driKey = if (link.startsWith("/")) { + // handle root related links + link.replace('/', '.').removePrefix(".") + } else { + val unSuffixedDri = dri.packageName!!.removeSuffix(".html").removeSuffix(".md") + val parentDri = unSuffixedDri.take(unSuffixedDri.indexOfLast('.'::equals)).removePrefix("_.") + "${parentDri}.${link.replace('/', '.')}" + } + allDRIs[driKey] + } + + val docTag = try { + parser.parse() + } catch (e: Throwable) { + val msg = "Error rendering (dri = $dri): ${e.message}" + println("ERROR: $msg") // TODO (#14): provide proper error handling + Text(msg, emptyList()) + } + + asContent(docTag, dri) + } else emptyList() + return PartiallyRenderedContent( + page, + nodes, + DCI(setOf(dri), ContentKind.Empty), + myDisplaySourceSet, + emptySet(), + PropertyContainer.empty() + ) + } + + fun asContent(d: DocTag, dri: DRI) = DocTagToContentConverter().buildContent( + d, + DCI(setOf(dri), ContentKind.Empty), + mySourceSet, + emptySet(), + PropertyContainer.empty() + ) + + fun loadFiles(files: List, customChildren: List = emptyList()): List { + val all = files.mapNotNull { loadTemplate(it) } + fun flatten(it: BaseStaticSiteProcessor.LoadedTemplate): List = + listOf(it.relativePath(root)) + it.children.flatMap { flatten(it) } + + fun pathToDri(path: String) = DRI("_.$path") + + val driMap = all.flatMap { flatten(it) }.map { it to pathToDri(it) }.toMap() + + fun templateToPage(myTemplate: BaseStaticSiteProcessor.LoadedTemplate): BaseStaticSiteProcessor.StaticPageNode { + val dri = pathToDri(myTemplate.relativePath(root)) + val page = try { + val properties = myTemplate.templateFile.layout() + ?.let { mapOf("content" to myTemplate.templateFile.rawCode) } ?: emptyMap() + + myTemplate.templateFile.resolveMarkdown(RenderingContext(properties, layouts)) + } catch (e: Throwable) { + val msg = "Error rendering $myTemplate: ${e.message}" + println("ERROR: $msg") // TODO (#14): provide proper error handling + PreResolvedPage("", null, true) + } + val content = parseMarkdown(page, dri, driMap) + val children = myTemplate.children.map { templateToPage(it) } + return BaseStaticSiteProcessor.StaticPageNode( + myTemplate.templateFile.title(), + children + customChildren, + myTemplate, + setOf(dri), + emptyList(), + content + ) + } + return all.map { templateToPage(it) } + } +} + diff --git a/src/main/kotlin/com/virtuslab/dokka/site/StaticSitePlugin.kt b/src/main/kotlin/com/virtuslab/dokka/site/StaticSitePlugin.kt index c40a40a..eca6c0c 100644 --- a/src/main/kotlin/com/virtuslab/dokka/site/StaticSitePlugin.kt +++ b/src/main/kotlin/com/virtuslab/dokka/site/StaticSitePlugin.kt @@ -2,26 +2,47 @@ package com.virtuslab.dokka.site import org.jetbrains.dokka.CoreExtensions import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.DokkaPlugin +import java.io.File class StaticSitePlugin : DokkaPlugin() { private val dokkaBase by lazy { plugin() } + private fun loadStaticSiteContext(cxt: DokkaContext): StaticSiteContext? = + cxt.configuration.pluginsConfiguration + .filter { it.fqPluginName == ExternalDocsTooKey } + .map { StaticSiteContext(File(it.values), cxt) } + .firstOrNull() + val customDocumentationProvider by extending { dokkaBase.htmlPreprocessors providing { ctx -> - SitePagesCreator(ctx) + SitePagesCreator(loadStaticSiteContext(ctx)) + } order { + before(dokkaBase.navigationPageInstaller) + before(dokkaBase.scriptsInstaller) + before(dokkaBase.stylesInstaller) + before(dokkaBase.packageListCreator) + } + } + + val customIndexRootProvider by extending { + dokkaBase.htmlPreprocessors providing { ctx -> + RootIndexPageCreator(loadStaticSiteContext(ctx)) } order { after(dokkaBase.navigationPageInstaller) - before(dokkaBase.styleAndScriptsAppender) + before(dokkaBase.scriptsInstaller) + before(dokkaBase.stylesInstaller) } } val customDocumentationResources by extending { dokkaBase.htmlPreprocessors providing { ctx -> - SiteResourceManager(ctx) + SiteResourceManager(loadStaticSiteContext(ctx)) } order { // We want our css and scripts after default ones - after(dokkaBase.styleAndScriptsAppender) + after(dokkaBase.scriptsInstaller) + before(dokkaBase.stylesInstaller) } } diff --git a/src/main/kotlin/com/virtuslab/dokka/site/compat.kt b/src/main/kotlin/com/virtuslab/dokka/site/compat.kt new file mode 100644 index 0000000..d2f426f --- /dev/null +++ b/src/main/kotlin/com/virtuslab/dokka/site/compat.kt @@ -0,0 +1,33 @@ +package com.virtuslab.dokka.site + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.Extension +import org.jetbrains.dokka.plugability.ExtensionBuilder +import org.jetbrains.dokka.plugability.OrderingKind +import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator + + +// TODO (#39): investigate if scala3doc can live without it +data class SourceSetWrapper(val sourceSet: DokkaConfiguration.DokkaSourceSet) { + fun toSet(): Set = setOf(sourceSet) + fun asMap(value: T): Map = mapOf(sourceSet to value) +} + +// TODO (#39): add fixes to in dokka +abstract class JavaSourceToDocumentableTranslator: SourceToDocumentableTranslator { + override suspend fun invoke(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext): DModule = + process(sourceSet, context) + + abstract fun process(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext): DModule +} + +// TODO (#39): fix that in dokka +class ExtensionBuilderEx { + fun newOrdering(old: ExtensionBuilder, before: Array>, after: Array>) = + old.copy(ordering = OrderingKind.ByDsl { + before(*before) + after(*after) + }) +} diff --git a/src/main/kotlin/com/virtuslab/dokka/site/locationProvider.kt b/src/main/kotlin/com/virtuslab/dokka/site/locationProvider.kt index 59991cb..324a2d7 100644 --- a/src/main/kotlin/com/virtuslab/dokka/site/locationProvider.kt +++ b/src/main/kotlin/com/virtuslab/dokka/site/locationProvider.kt @@ -3,6 +3,7 @@ package com.virtuslab.dokka.site 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.ContentPage import org.jetbrains.dokka.pages.PageNode import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.DokkaContext @@ -14,16 +15,18 @@ class StaticSiteLocationProviderFactory(private val ctx: DokkaContext) : Locatio } class StaticSiteLocationProvider(ctx: DokkaContext, pageNode: RootPageNode) : DokkaLocationProvider(pageNode, ctx) { - override fun pathTo(node: PageNode, context: PageNode?): String = - if (node is BaseStaticSiteProcessor.StaticPageNode) - if (node.dri.contains(docsRootDRI)) "index" - else { - // replace title with original markdown file name - val original = super.pathTo(node, context) - val paths = original.split("/") - val fileName = node.loadedTemplate.file.name - (paths.dropLast(1) + listOf(fileName)).joinToString("/") - } - else - super.pathTo(node, context) + private fun updatePageEntry(path: List, page: PageNode): List = + if (page is BaseStaticSiteProcessor.StaticPageNode){ + if (page.dri.contains(docsRootDRI)) listOf("index") + else { + val start = if (path[0] == "--root--") listOf("docs") else path.take(1) + start + path.drop(1).dropLast(1) + listOf(page.loadedTemplate.file.nameWithoutExtension) + } + } + else if (page is ContentPage && page.dri.contains(docsDRI)) listOf("docs") + else if (page is ContentPage && page.dri.contains(apiPageDRI)) listOf("api", "index") + else if (path.size > 1 && path[0] == "--root--" && path[1] == "-a-p-i") listOf("api") + path.drop(2) + else path + + override val pathsIndex: Map> = super.pathsIndex.mapValues { updatePageEntry(it.value, it.key) } } diff --git a/src/main/kotlin/com/virtuslab/dokka/site/processors.kt b/src/main/kotlin/com/virtuslab/dokka/site/processors.kt index 417a363..7aacefd 100644 --- a/src/main/kotlin/com/virtuslab/dokka/site/processors.kt +++ b/src/main/kotlin/com/virtuslab/dokka/site/processors.kt @@ -2,34 +2,25 @@ package com.virtuslab.dokka.site import org.jetbrains.dokka.base.renderers.html.NavigationNode import org.jetbrains.dokka.base.renderers.html.NavigationPage -import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.doc.Text -import org.jetbrains.dokka.model.properties.PropertyContainer -import org.jetbrains.dokka.model.toDisplaySourceSet import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.pages.PageTransformer import java.io.File const val ExternalDocsTooKey = "ExternalDocsTooKey" val docsRootDRI = DRI.topLevel.copy(extra = "_top_level_index") +val docsDRI = DRI.topLevel.copy(extra = "_docs_level_index") +val apiPageDRI = DRI(classNames = "api", extra = "__api__") -abstract class BaseStaticSiteProcessor(cxt: DokkaContext) : PageTransformer { +abstract class BaseStaticSiteProcessor(private val staticSiteContext: StaticSiteContext?) : PageTransformer { final override fun invoke(input: RootPageNode): RootPageNode = - rawRoot?.let { transform(input) } ?: input + staticSiteContext?.let { transform(input, it) } ?: input - protected abstract fun transform(input: RootPageNode): RootPageNode - - private val rawRoot: File? = cxt.configuration.pluginsConfiguration.get(ExternalDocsTooKey)?.let { File(it) } - val root = rawRoot ?: File("unknown") - - protected val mySourceSet = cxt.configuration.sourceSets.toSet() - protected val myDisplaySourceSet = mySourceSet.map { it.toDisplaySourceSet() }.toSet() - protected val docsFile = File(root, "docs") + protected abstract fun transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode data class LoadedTemplate(val templateFile: TemplateFile, val children: List, val file: File) { fun isIndexPage() = file.isFile && (file.name == "index.md" || file.name == "index.html") @@ -39,7 +30,7 @@ abstract class BaseStaticSiteProcessor(cxt: DokkaContext) : PageTransformer { data class StaticPageNode( override val name: String, - override val children: List, + override val children: List, val loadedTemplate: LoadedTemplate, override val dri: Set, override val embeddedResources: List = emptyList(), @@ -62,10 +53,10 @@ abstract class BaseStaticSiteProcessor(cxt: DokkaContext) : PageTransformer { content = content, dri = dri, embeddedResources = embeddedResources, - children = children.map { it as StaticPageNode }) + children = children + ) - override fun modified(name: String, children: List): PageNode = - copy(name = name, children = children.map { it as StaticPageNode }) + override fun modified(name: String, children: List): PageNode = copy(name = name, children = children) fun resources() = when (content) { is PartiallyRenderedContent -> @@ -77,7 +68,7 @@ abstract class BaseStaticSiteProcessor(cxt: DokkaContext) : PageTransformer { } } -class SiteResourceManager(cxt: DokkaContext) : BaseStaticSiteProcessor(cxt) { +class SiteResourceManager(ctx: StaticSiteContext?) : BaseStaticSiteProcessor(ctx) { private fun listResources(nodes: List): Set = nodes.flatMap { when (it) { @@ -86,12 +77,12 @@ class SiteResourceManager(cxt: DokkaContext) : BaseStaticSiteProcessor(cxt) { } }.toSet() - override fun transform(input: RootPageNode): RootPageNode { - val images = File(root, "images").walkTopDown().filter { it.isFile } - .map { root.toPath().relativize(it.toPath()).toString() } + override fun transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode { + val images = File(ctx.root, "images").walkTopDown().filter { it.isFile } + .map { ctx.root.toPath().relativize(it.toPath()).toString() } val resources = listResources(input.children) + images val resourcePages = resources.map { path -> - RendererSpecificResourcePage(path, emptyList(), RenderingStrategy.Write(File(root, path).readText())) + RendererSpecificResourcePage(path, emptyList(), RenderingStrategy.Write(File(ctx.root, path).readText())) } val modified = input.transformContentPagesTree { when (it) { @@ -103,179 +94,93 @@ class SiteResourceManager(cxt: DokkaContext) : BaseStaticSiteProcessor(cxt) { } } -class SitePagesCreator(cxt: DokkaContext) : BaseStaticSiteProcessor(cxt) { - - override fun transform(input: RootPageNode): RootPageNode { - val (navigationPage, rest) = input.children.partition { it is NavigationPage } - val defaultNavigation = (navigationPage.single() as NavigationPage).root - - val allFiles = docsFile.listFiles()?.toList() ?: emptyList() - val (indexes, children) = loadFiles(allFiles).partition { it.loadedTemplate.isIndexPage() } - if (indexes.size > 1) println("ERROR: Multiple index pages found $indexes}") // TODO (#14): provide proper error handling +data class AContentPage( + override val name: String, + override val children: List, + override val content: ContentNode, + override val dri: Set, + override val embeddedResources: List = emptyList(), +) : ContentPage { + override val documentable: Documentable? = null + + override fun modified( + name: String, + content: ContentNode, + dri: Set, + embeddedResources: List, + children: List + ): ContentPage = copy(name, children, content, dri, embeddedResources) + + override fun modified(name: String, children: List): PageNode = copy(name, children = children) +} - fun toNavigationNode(c: StaticPageNode): NavigationNode = - NavigationNode( - c.loadedTemplate.templateFile.title(), - c.dri.first(), - myDisplaySourceSet, - c.children.map { toNavigationNode(it) } - ) - val apiPageDri = DRI.topLevel.copy(extra = "_api_") +class SitePagesCreator(ctx: StaticSiteContext?) : BaseStaticSiteProcessor(ctx) { - val mergedRoots = NavigationNode( - defaultNavigation.name, - defaultNavigation.dri, - defaultNavigation.sourceSets, - children.map { toNavigationNode(it) } + listOf( - NavigationNode( - "API", - apiPageDri, - defaultNavigation.sourceSets, - defaultNavigation.children + private fun processRootPage(input: RootPageNode, children: List = emptyList()): AContentPage = + when (input) { + is ContentPage -> + AContentPage( + input.name, + children, + input.content, + setOf(apiPageDRI), + input.embeddedResources ) - ) - ) - val original = indexes.firstOrNull()?.let { indexPage -> - rest.map { - when (it) { - is ModulePageNode -> - if (it.dri.contains(DRI.topLevel)) { - val packageList = - PackagePageNode( - "_root_", - it.content, - setOf(apiPageDri), - null, - emptyList(), - it.embeddedResources - ) - it.modified(content = indexPage.content, children = it.children + listOf(packageList)) - } else it - else -> - it - } - } - } ?: rest - - val indexFiles = listOf(File(root, "index.html"), File(root, "index.md")).filter { it.exists() } - if (indexFiles.size > 1) println("ERROR: Multiple root index pages found: ${indexFiles.map { it.absolutePath }}") // TODO (#14): provide proper error handling - - val topLevelIndexPage = loadFiles(indexFiles.take(1)).map { it.modified(dri = setOf(docsRootDRI)) } - - return input.modified(children = original + topLevelIndexPage + listOf(NavigationPage(mergedRoots)) + children) - } - - private val layouts: Map by lazy { - val layoutRoot = File(root, "_layouts") - val dirs: Array = layoutRoot.listFiles() ?: emptyArray() - dirs.map { loadTemplateFile(it) }.map { it.name() to it }.toMap() - } - - - private fun isValidTemplate(file: File): Boolean = - (file.isDirectory && !file.name.startsWith("_")) || - file.name.endsWith(".md") || - file.name.endsWith(".html") - - - private fun loadTemplate(from: File): LoadedTemplate? = - if (!isValidTemplate(from)) null else try { - val (indexes, children) = (from.listFiles()?.mapNotNull { loadTemplate(it) } - ?: emptyList()).partition { it.isIndexPage() } - if (indexes.size > 1) - println("ERROR: Multiple index pages for $from found in ${indexes.map { it.file }}") // TODO (#14): provide proper error handling - - fun loadIndexPage(): TemplateFile { - val indexFiles = from.listFiles { file -> file.name == "index.md" || file.name == "index.html" } - return when (indexFiles.size) { - 0 -> emptyTemplate(from) - 1 -> loadTemplateFile(indexFiles.first()).copy(file = from) - else -> throw java.lang.RuntimeException("ERROR: Multiple index pages found under ${from.path}") + is RendererSpecificRootPage -> + children.filterIsInstance().single().let { nestedRoot -> + processRootPage( + nestedRoot, + children.filter { it != nestedRoot } + nestedRoot.children) } - } - - val templateFile = if (from.isDirectory) loadIndexPage() else loadTemplateFile(from) - - LoadedTemplate(templateFile, children, from) - - } catch (e: RuntimeException) { - e.printStackTrace() // TODO (#14): provide proper error handling - null + else -> TODO("UNSUPPORTED! ${input.javaClass.name}") } - private fun parseMarkdown(page: PreResolvedPage, dri: DRI, allDRIs: Map): ContentNode { - val nodes = if (page.hasMarkdown) { - val parser = ExtendableMarkdownParser(page.code) { link -> - val driKey = if (link.startsWith("/")) { - // handle root related links - link.replace('/', '.').removePrefix(".") - } else { - val unSuffixedDri = dri.packageName!!.removeSuffix(".html").removeSuffix(".md") - val parentDri = unSuffixedDri.take(unSuffixedDri.indexOfLast('.'::equals)).removePrefix("_.") - "${parentDri}.${link.replace('/', '.')}" - } - allDRIs[driKey] - } + override fun transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode { + val (contentPage, others) = input.children.partition { it is ContentPage } + val modifiedModuleRoot = processRootPage(input, contentPage) + val allFiles = ctx.docsFile.listFiles()?.toList() ?: emptyList() + val (indexes, children) = ctx.loadFiles(allFiles).partition { it.loadedTemplate.isIndexPage() } + if (indexes.size > 1) println("ERROR: Multiple index pages found $indexes}") // TODO (#14): provide proper error handling - val docTag = try { - parser.parse() - } catch (e: Throwable) { - val msg = "Error rendering (dri = $dri): ${e.message}" - println("ERROR: $msg") // TODO (#14): provide proper error handling - Text(msg, emptyList()) - } + val rootContent = + indexes.map { it.content }.firstOrNull() ?: ctx.asContent(Text(), DRI(extra = "root_content"))[0] - DocTagToContentConverter.buildContent( - docTag, - DCI(setOf(dri), ContentKind.Empty), - mySourceSet, - emptySet(), - PropertyContainer.empty() - ) - } else emptyList() - return PartiallyRenderedContent( - page, - nodes, - DCI(setOf(dri), ContentKind.Empty), - myDisplaySourceSet, - emptySet(), - PropertyContainer.empty() + val root = AContentPage( + input.name, + listOf(modifiedModuleRoot.modified(name = "API")) + children, + rootContent, + setOf(docsDRI), + emptyList() ) - } - - private fun loadFiles(files: List): List { - val all = files.mapNotNull { loadTemplate(it) } - fun flatten(it: LoadedTemplate): List = - listOf(it.relativePath(root)) + it.children.flatMap { flatten(it) } - - fun pathToDri(path: String) = DRI("_.$path") - val driMap = all.flatMap { flatten(it) }.map { it to pathToDri(it) }.toMap() + return RendererSpecificRootPage( + modifiedModuleRoot.name, + listOf(root) + others, + RenderingStrategy.DoNothing + ) + } - fun templateToPage(myTemplate: LoadedTemplate): StaticPageNode { - val dri = pathToDri(myTemplate.relativePath(root)) - val page = try { - val properties = myTemplate.templateFile.layout() - ?.let { mapOf("content" to myTemplate.templateFile.rawCode) } ?: emptyMap() +} - myTemplate.templateFile.resolveMarkdown(RenderingContext(properties, layouts)) - } catch (e: Throwable) { - val msg = "Error rendering $myTemplate: ${e.message}" - println("ERROR: $msg") // TODO (#14): provide proper error handling - PreResolvedPage("", null, true) +class RootIndexPageCreator(ctx: StaticSiteContext?) : BaseStaticSiteProcessor(ctx) { + override fun transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode = + ctx.indexPage()?.let { + val (contentNodes, nonContent) = input.children.partition { it is ContentNode } + val (navigations, rest) = nonContent.partition { it is NavigationPage } + val modifiedNavigation = navigations.map { + val root = (it as NavigationPage).root + val (api, rest) = root.children.partition { it.dri == apiPageDRI } + NavigationPage( + NavigationNode( + input.name, + docsRootDRI, + root.sourceSets, + rest + api + ) + ) } - val content = parseMarkdown(page, dri, driMap) - val children = myTemplate.children.map { templateToPage(it) } - return StaticPageNode( - myTemplate.templateFile.title(), - children, - myTemplate, - setOf(dri), - emptyList(), - content - ) - } - return all.map { templateToPage(it) } - } + val newRoot = it.modified(dri = setOf(docsRootDRI), children = contentNodes) + input.modified(children = listOf(newRoot) + rest + modifiedNavigation) + } ?: input } diff --git a/src/main/kotlin/com/virtuslab/dokka/site/renderer.kt b/src/main/kotlin/com/virtuslab/dokka/site/renderer.kt index bf0552a..f3a7908 100644 --- a/src/main/kotlin/com/virtuslab/dokka/site/renderer.kt +++ b/src/main/kotlin/com/virtuslab/dokka/site/renderer.kt @@ -2,6 +2,7 @@ package com.virtuslab.dokka.site import kotlinx.html.* import kotlinx.html.stream.createHTML +import org.jetbrains.dokka.base.renderers.isImage import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext import java.net.URI @@ -63,8 +64,9 @@ open class SiteRenderer(context: DokkaContext) : org.jetbrains.dokka.base.render createHTML().html { head { meta(name = "viewport", content = "width=device-width, initial-scale=1", charset = "UTF-8") - title(page.loadedTemplate.templateFile.title()) - (if (page.hasFrame()) resources else page.resources()).forEach { + title(page.name) + link(href = page.root("images/logo-icon.svg"), rel = "icon", type = "image/svg") + resources.forEach { when { it.substringBefore('?').substringAfterLast('.') == "css" -> link( rel = LinkRel.stylesheet, @@ -76,6 +78,7 @@ open class SiteRenderer(context: DokkaContext) : org.jetbrains.dokka.base.render ) { async = true } + it.isImage() -> link(href = page.root(it)) else -> unsafe { +it } } } @@ -83,10 +86,12 @@ open class SiteRenderer(context: DokkaContext) : org.jetbrains.dokka.base.render } body { if (page.hasFrame()) defaultFrame(page, content) else buildPageContent(this, page) + } } - private fun FlowContent.defaultFrame(page: PageNode, content: FlowContent.() -> Unit): Unit = div { + private fun FlowContent.defaultFrame(page: PageNode, content: FlowContent.() -> Unit): Unit = + div { id = "container" div { id = "leftColumn" @@ -104,6 +109,10 @@ open class SiteRenderer(context: DokkaContext) : org.jetbrains.dokka.base.render } div { id = "main" + div { + id = "leftToggler" + span("icon-toggler") + } div { id = "searchBar" }