aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src/main/kotlin/generation/SingleModuleGeneration.kt
blob: 5cdeeb40f3d0b1059613a34d34b76f391242f199 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package org.jetbrains.dokka.base.generation

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.jetbrains.dokka.CoreExtensions
import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.DokkaException
import org.jetbrains.dokka.Timer
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.generation.Generation
import org.jetbrains.dokka.generation.exitGenerationGracefully
import org.jetbrains.dokka.model.DModule
import org.jetbrains.dokka.pages.RootPageNode
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.plugin
import org.jetbrains.dokka.plugability.query
import org.jetbrains.dokka.transformers.sources.AsyncSourceToDocumentableTranslator
import org.jetbrains.dokka.utilities.parallelMap
import org.jetbrains.dokka.utilities.report

class SingleModuleGeneration(private val context: DokkaContext) : Generation {

    override fun Timer.generate() {
        report("Validity check")
        validityCheck(context)

        report("Creating documentation models")
        val modulesFromPlatforms = createDocumentationModels()

        report("Transforming documentation model before merging")
        val transformedDocumentationBeforeMerge = transformDocumentationModelBeforeMerge(modulesFromPlatforms)

        report("Merging documentation models")
        val documentationModel = mergeDocumentationModels(transformedDocumentationBeforeMerge)
            ?: exitGenerationGracefully("Nothing to document")

        report("Transforming documentation model after merging")
        val transformedDocumentation = transformDocumentationModelAfterMerge(documentationModel)

        report("Creating pages")
        val pages = createPages(transformedDocumentation)

        report("Transforming pages")
        val transformedPages = transformPages(pages)

        report("Rendering")
        render(transformedPages)

        report("Running post-actions")
        runPostActions()

        reportAfterRendering()
    }

    override val generationName = "documentation for ${context.configuration.moduleName}"

    fun createDocumentationModels() = runBlocking(Dispatchers.Default) {
        context.configuration.sourceSets.parallelMap { sourceSet -> translateSources(sourceSet, context) }.flatten()
            .also { modules -> if (modules.isEmpty()) exitGenerationGracefully("Nothing to document") }
    }

    fun transformDocumentationModelBeforeMerge(modulesFromPlatforms: List<DModule>) =
        context.plugin<DokkaBase>().query { preMergeDocumentableTransformer }
            .fold(modulesFromPlatforms) { acc, t -> t(acc) }

    fun mergeDocumentationModels(modulesFromPlatforms: List<DModule>) =
        context.single(CoreExtensions.documentableMerger).invoke(modulesFromPlatforms)

    fun transformDocumentationModelAfterMerge(documentationModel: DModule) =
        context[CoreExtensions.documentableTransformer].fold(documentationModel) { acc, t -> t(acc, context) }

    fun createPages(transformedDocumentation: DModule) =
        context.single(CoreExtensions.documentableToPageTranslator).invoke(transformedDocumentation)

    fun transformPages(pages: RootPageNode) =
        context[CoreExtensions.pageTransformer].fold(pages) { acc, t -> t(acc) }

    fun render(transformedPages: RootPageNode) {
        context.single(CoreExtensions.renderer).render(transformedPages)
    }

    fun runPostActions() {
        context[CoreExtensions.postActions].forEach { it() }
    }

    fun validityCheck(context: DokkaContext) {
        val (preGenerationCheckResult, checkMessages) = context[CoreExtensions.preGenerationCheck].fold(
            Pair(true, emptyList<String>())
        ) { acc, checker -> checker() + acc }
        if (!preGenerationCheckResult) throw DokkaException(
            "Pre-generation validity check failed: ${checkMessages.joinToString(",")}"
        )
    }

    fun reportAfterRendering() {
        context.unusedPoints.takeIf { it.isNotEmpty() }?.also {
            context.logger.info("Unused extension points found: ${it.joinToString(", ")}")
        }

        context.logger.report()

        if (context.configuration.failOnWarning && (context.logger.warningsCount > 0 || context.logger.errorsCount > 0)) {
            throw DokkaException(
                "Failed with warningCount=${context.logger.warningsCount} and errorCount=${context.logger.errorsCount}"
            )
        }
    }

    private suspend fun translateSources(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext) =
        context[CoreExtensions.sourceToDocumentableTranslator].parallelMap { translator ->
            when (translator) {
                is AsyncSourceToDocumentableTranslator -> translator.invokeSuspending(sourceSet, context)
                else -> translator.invoke(sourceSet, context)
            }
        }
}