Wizard: add simple js template
This commit is contained in:
+9
@@ -5,6 +5,9 @@ import io.ktor.routing.get
|
|||||||
import io.ktor.routing.routing
|
import io.ktor.routing.routing
|
||||||
import io.ktor.server.engine.embeddedServer
|
import io.ktor.server.engine.embeddedServer
|
||||||
import ${serverEngine.getImport()}
|
import ${serverEngine.getImport()}
|
||||||
|
#foreach($import in $imports)
|
||||||
|
import $import
|
||||||
|
#end
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
|
|
||||||
fun HTML.index() {
|
fun HTML.index() {
|
||||||
@@ -15,6 +18,9 @@ fun HTML.index() {
|
|||||||
div {
|
div {
|
||||||
+"Hello from Ktor"
|
+"Hello from Ktor"
|
||||||
}
|
}
|
||||||
|
#foreach($element in $elements)
|
||||||
|
$element
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,6 +30,9 @@ fun main() {
|
|||||||
get("/") {
|
get("/") {
|
||||||
call.respondHtml(HttpStatusCode.OK, HTML::index)
|
call.respondHtml(HttpStatusCode.OK, HTML::index)
|
||||||
}
|
}
|
||||||
|
#foreach($route in $routes)
|
||||||
|
$route
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
}.start(wait = true)
|
}.start(wait = true)
|
||||||
}
|
}
|
||||||
+1
-1
@@ -13,7 +13,7 @@ sealed class Identificator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GeneratedIdentificator(prefix: String?) : Identificator() {
|
class GeneratedIdentificator(prefix: String?) : Identificator() {
|
||||||
override val id = """${prefix.orEmpty()}_${RandomIdGenerator.generate()}"""
|
override val id = "${prefix.orEmpty()}_${RandomIdGenerator.generate()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ fun <I : IrsOwner> I.withIrs(vararg irs: BuildSystemIR) = withReplacedIrs(irs =
|
|||||||
|
|
||||||
|
|
||||||
inline fun <reified I : BuildSystemIR> IrsOwner.irsOfType(): List<I> =
|
inline fun <reified I : BuildSystemIR> IrsOwner.irsOfType(): List<I> =
|
||||||
irs.filterIsInstance<I>()
|
irs.filterIsInstance<I>()
|
||||||
|
|
||||||
|
|
||||||
inline fun <reified I : BuildSystemIR> IrsOwner.irsOfTypeOrNull() =
|
inline fun <reified I : BuildSystemIR> IrsOwner.irsOfTypeOrNull() =
|
||||||
|
|||||||
+16
@@ -50,6 +50,22 @@ data class GradleCallIr(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class GradleNewInstanceCall(
|
||||||
|
val name: String,
|
||||||
|
val parameters: List<BuildSystemIR> = emptyList()
|
||||||
|
) : GradleIR {
|
||||||
|
override fun GradlePrinter.renderGradle() {
|
||||||
|
if (dsl == GradlePrinter.GradleDsl.GROOVY) {
|
||||||
|
+"new "
|
||||||
|
}
|
||||||
|
call(name, forceBrackets = true) {
|
||||||
|
parameters.list { it.render(this) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
data class GradleSectionIR(
|
data class GradleSectionIR(
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|||||||
+34
-7
@@ -1,14 +1,16 @@
|
|||||||
package org.jetbrains.kotlin.tools.projectWizard.plugins.templates
|
package org.jetbrains.kotlin.tools.projectWizard.plugins.templates
|
||||||
|
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.core.*
|
import org.jetbrains.kotlin.tools.projectWizard.core.*
|
||||||
|
import org.jetbrains.kotlin.tools.projectWizard.core.service.FileSystemWizardService
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
|
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemPlugin
|
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemPlugin
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.updateBuildFiles
|
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.updateBuildFiles
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.templates.*
|
import org.jetbrains.kotlin.tools.projectWizard.templates.*
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.TemplateInterceptor
|
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.InterceptionPoint
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.fold
|
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.TemplateInterceptionApplicationState
|
||||||
|
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.applyAll
|
||||||
|
|
||||||
class TemplatesPlugin(context: Context) : Plugin(context) {
|
class TemplatesPlugin(context: Context) : Plugin(context) {
|
||||||
val templates by property<Map<String, Template>>(
|
val templates by property<Map<String, Template>>(
|
||||||
@@ -101,12 +103,37 @@ class TemplatesPlugin(context: Context) : Plugin(context) {
|
|||||||
|
|
||||||
withAction {
|
withAction {
|
||||||
updateBuildFiles { buildFile ->
|
updateBuildFiles { buildFile ->
|
||||||
@Suppress("UNCHECKED_CAST")
|
val sourcesets = buildFile.sourcesets
|
||||||
val interceptors = buildFile.sourcesets.mapNotNull { sourceset ->
|
|
||||||
sourceset.template?.createInterceptors(sourceset) as? List<TemplateInterceptor>
|
val applicationState = sourcesets.mapNotNull { sourceset ->
|
||||||
}.flatten().fold()
|
sourceset.template?.createInterceptors(sourceset)
|
||||||
interceptors.applyUntilConverge(buildFile).asSuccess()
|
}.flatten()
|
||||||
|
.applyAll(TemplateInterceptionApplicationState(buildFile, emptyMap()))
|
||||||
|
|
||||||
|
val templateEngine = VelocityTemplateEngine()
|
||||||
|
|
||||||
|
val templatesApplicationResult = sourcesets.map { sourceset ->
|
||||||
|
val settings = applicationState.sourcesetToSettings[sourceset.original.identificator].orEmpty()
|
||||||
|
applyFileTemplatesFromSourceset(sourceset, templateEngine, settings)
|
||||||
|
}.sequenceIgnore()
|
||||||
|
|
||||||
|
templatesApplicationResult andThen applicationState.buildFileIR.asSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun TaskRunningContext.applyFileTemplatesFromSourceset(
|
||||||
|
sourceset: SourcesetIR,
|
||||||
|
templateEngine: TemplateEngine,
|
||||||
|
interceptionPointSettings: Map<InterceptionPoint<Any>, Any>
|
||||||
|
): TaskResult<Unit> {
|
||||||
|
val template = sourceset.template ?: return UNIT_SUCCESS
|
||||||
|
val settings = with(template) { settingsAsMap(sourceset.original) }
|
||||||
|
val allSettings = settings + interceptionPointSettings.mapKeys { it.key.name }
|
||||||
|
return with(template) { getFileTemplates(sourceset) }.map { fileTemplate ->
|
||||||
|
val fileText = templateEngine.renderTemplate(fileTemplate, allSettings)
|
||||||
|
val path = sourceset.path / fileTemplate.relativePath
|
||||||
|
service<FileSystemWizardService>()!!.createFile(path, fileText)
|
||||||
|
}.sequenceIgnore()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+6
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.DefaultRepo
|
|||||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Repositories
|
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Repositories
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
|
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||||
|
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.InterceptionPoint
|
||||||
|
|
||||||
class KtorServerTemplate : Template() {
|
class KtorServerTemplate : Template() {
|
||||||
override val title: String = "Ktor-based Server"
|
override val title: String = "Ktor-based Server"
|
||||||
@@ -55,6 +56,11 @@ class KtorServerTemplate : Template() {
|
|||||||
|
|
||||||
val serverEngine by enumSetting<KtorServerEngine>("Ktor Server", GenerationPhase.PROJECT_GENERATION)
|
val serverEngine by enumSetting<KtorServerEngine>("Ktor Server", GenerationPhase.PROJECT_GENERATION)
|
||||||
|
|
||||||
|
val imports = InterceptionPoint("imports", emptyList<String>())
|
||||||
|
val routes = InterceptionPoint("routes", emptyList<String>())
|
||||||
|
val elements = InterceptionPoint("elements", emptyList<String>())
|
||||||
|
|
||||||
|
override val interceptionPoints: List<InterceptionPoint<Any>> = listOf(imports, routes, elements)
|
||||||
override val settings: List<TemplateSetting<*, *>> = listOf(serverEngine)
|
override val settings: List<TemplateSetting<*, *>> = listOf(serverEngine)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+34
-5
@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
|
|||||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.DefaultRepository
|
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.DefaultRepository
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
|
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.Interceptor
|
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.TemplateInterceptor
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.interceptTemplate
|
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.interceptTemplate
|
||||||
|
|
||||||
class SimpleJsClientTemplate : Template() {
|
class SimpleJsClientTemplate : Template() {
|
||||||
@@ -40,13 +40,42 @@ class SimpleJsClientTemplate : Template() {
|
|||||||
FileTemplateDescriptor("$id/client.kt.vm", sourcesPath("client.kt"))
|
FileTemplateDescriptor("$id/client.kt.vm", sourcesPath("client.kt"))
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun createInterceptors(sourceset: SourcesetIR): List<Interceptor> = buildList {
|
override fun createInterceptors(sourceset: SourcesetIR): List<TemplateInterceptor> = buildList {
|
||||||
+interceptTemplate(KtorServerTemplate()) {
|
+interceptTemplate(KtorServerTemplate()) {
|
||||||
applicableIf { buildFileIR ->
|
applicableIf { buildFileIR ->
|
||||||
val tasks = buildFileIR.irsOfTypeOrNull<GradleConfigureTaskIR>() ?: return@applicableIf false
|
val tasks = buildFileIR.irsOfTypeOrNull<GradleConfigureTaskIR>() ?: return@applicableIf false
|
||||||
tasks.none { it.taskAccess.name.endsWith("Jar") }
|
tasks.none { it.taskAccess.name.endsWith("Jar") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interceptAtPoint(template.routes) { value ->
|
||||||
|
if (value.isNotEmpty()) return@interceptAtPoint value
|
||||||
|
buildList {
|
||||||
|
+value
|
||||||
|
+"""
|
||||||
|
static("/static") {
|
||||||
|
resources()
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interceptAtPoint(template.imports) { value ->
|
||||||
|
if (value.isNotEmpty()) return@interceptAtPoint value
|
||||||
|
buildList {
|
||||||
|
+value
|
||||||
|
+"io.ktor.http.content.resources"
|
||||||
|
+"io.ktor.http.content.static"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interceptAtPoint(template.elements) { value ->
|
||||||
|
if (value.isNotEmpty()) return@interceptAtPoint value
|
||||||
|
buildList {
|
||||||
|
+value
|
||||||
|
+"""script(src = "/static/output.js") {}"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
transformBuildFile { buildFileIR ->
|
transformBuildFile { buildFileIR ->
|
||||||
val jsSourcesetName = sourceset.safeAs<SourcesetModuleIR>()?.targetName ?: return@transformBuildFile null
|
val jsSourcesetName = sourceset.safeAs<SourcesetModuleIR>()?.targetName ?: return@transformBuildFile null
|
||||||
val jvmTarget = buildFileIR.targets.firstOrNull { target ->
|
val jvmTarget = buildFileIR.targets.firstOrNull { target ->
|
||||||
@@ -64,7 +93,7 @@ class SimpleJsClientTemplate : Template() {
|
|||||||
val from = GradleCallIr(
|
val from = GradleCallIr(
|
||||||
"from",
|
"from",
|
||||||
listOf(
|
listOf(
|
||||||
GradleCallIr(
|
GradleNewInstanceCall(
|
||||||
"File",
|
"File",
|
||||||
listOf(
|
listOf(
|
||||||
GradlePropertyAccessIR("$webPackTaskName.destinationDirectory"),
|
GradlePropertyAccessIR("$webPackTaskName.destinationDirectory"),
|
||||||
@@ -120,7 +149,7 @@ class SimpleJsClientTemplate : Template() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val JS_OUTPUT_FILE_NAME = "output.js"
|
private const val JS_OUTPUT_FILE_NAME = "output.js"
|
||||||
const val WEBPACK_TASK_CLASS = "KotlinWebpack"
|
private const val WEBPACK_TASK_CLASS = "KotlinWebpack"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+8
-17
@@ -4,8 +4,6 @@ import org.jetbrains.kotlin.tools.projectWizard.Identificator
|
|||||||
import org.jetbrains.kotlin.tools.projectWizard.SettingsOwner
|
import org.jetbrains.kotlin.tools.projectWizard.SettingsOwner
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.core.*
|
import org.jetbrains.kotlin.tools.projectWizard.core.*
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.core.service.FileSystemWizardService
|
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.Interceptor
|
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
|
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.TargetConfigurationIR
|
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.TargetConfigurationIR
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||||
@@ -15,6 +13,8 @@ import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
|||||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset
|
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
|
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||||
|
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.InterceptionPoint
|
||||||
|
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.TemplateInterceptor
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
|
||||||
@@ -64,6 +64,7 @@ abstract class Template : SettingsOwner {
|
|||||||
abstract val sourcesetTypes: Set<SourcesetType>
|
abstract val sourcesetTypes: Set<SourcesetType>
|
||||||
|
|
||||||
open val settings: List<TemplateSetting<*, *>> = emptyList()
|
open val settings: List<TemplateSetting<*, *>> = emptyList()
|
||||||
|
open val interceptionPoints: List<InterceptionPoint<Any>> = emptyList()
|
||||||
|
|
||||||
open fun TaskRunningContext.getRequiredLibraries(sourceset: SourcesetIR): List<DependencyIR> = emptyList()
|
open fun TaskRunningContext.getRequiredLibraries(sourceset: SourcesetIR): List<DependencyIR> = emptyList()
|
||||||
|
|
||||||
@@ -79,16 +80,11 @@ abstract class Template : SettingsOwner {
|
|||||||
|
|
||||||
open fun TaskRunningContext.getFileTemplates(sourceset: SourcesetIR): List<FileTemplateDescriptor> = emptyList()
|
open fun TaskRunningContext.getFileTemplates(sourceset: SourcesetIR): List<FileTemplateDescriptor> = emptyList()
|
||||||
|
|
||||||
open fun createInterceptors(sourceset: SourcesetIR): List<Interceptor> = emptyList()
|
open fun createInterceptors(sourceset: SourcesetIR): List<TemplateInterceptor> = emptyList()
|
||||||
|
|
||||||
fun TaskRunningContext.applyToSourceset(
|
fun TaskRunningContext.applyToSourceset(
|
||||||
templateEngine: TemplateEngine,
|
|
||||||
sourceset: SourcesetIR
|
sourceset: SourcesetIR
|
||||||
): TaskResult<TemplateApplicationResult> {
|
): TaskResult<TemplateApplicationResult> {
|
||||||
val fileSystemService = service<FileSystemWizardService>()!!
|
|
||||||
|
|
||||||
val allSettings = createDefaultSettings() + settingsAsMap(sourceset.original)
|
|
||||||
|
|
||||||
val librariesToAdd = getRequiredLibraries(sourceset)
|
val librariesToAdd = getRequiredLibraries(sourceset)
|
||||||
val irsToAddToBuildFile = getIrsToAddToBuildFile(sourceset)
|
val irsToAddToBuildFile = getIrsToAddToBuildFile(sourceset)
|
||||||
|
|
||||||
@@ -101,20 +97,15 @@ abstract class Template : SettingsOwner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val result = TemplateApplicationResult(librariesToAdd, irsToAddToBuildFile, targetsUpdater)
|
val result = TemplateApplicationResult(librariesToAdd, irsToAddToBuildFile, targetsUpdater)
|
||||||
|
return result.asSuccess()
|
||||||
return getFileTemplates(sourceset).map { fileTemplate ->
|
|
||||||
val fileText = templateEngine.renderTemplate(fileTemplate, allSettings)
|
|
||||||
val path = sourceset.path / fileTemplate.relativePath
|
|
||||||
fileSystemService.createFile(path, fileText)
|
|
||||||
}.sequenceIgnore() andThen result.asSuccess()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TaskRunningContext.settingsAsMap(sourceset: Sourceset): Map<String, Any> =
|
fun TaskRunningContext.settingsAsMap(sourceset: Sourceset): Map<String, Any> =
|
||||||
withSettingsOf(sourceset) {
|
withSettingsOf(sourceset) {
|
||||||
settings.associate { setting ->
|
settings.associate { setting ->
|
||||||
setting.path to setting.reference.settingValue
|
setting.path to setting.reference.settingValue
|
||||||
}
|
}
|
||||||
}
|
} + createDefaultSettings()
|
||||||
|
|
||||||
|
|
||||||
private fun TaskRunningContext.createDefaultSettings() = mapOf(
|
private fun TaskRunningContext.createDefaultSettings() = mapOf(
|
||||||
@@ -250,7 +241,7 @@ fun TaskRunningContext.applyTemplateToSourceset(
|
|||||||
): TaskResult<TemplateApplicationResult> = when (template) {
|
): TaskResult<TemplateApplicationResult> = when (template) {
|
||||||
null -> TemplateApplicationResult.EMPTY.asSuccess()
|
null -> TemplateApplicationResult.EMPTY.asSuccess()
|
||||||
else -> with(template) {
|
else -> with(template) {
|
||||||
applyToSourceset(templateEngine, sourceset)
|
applyToSourceset(sourceset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors
|
||||||
|
|
||||||
|
data class InterceptionPoint<out T: Any>(val name: String, val initialValue: T)
|
||||||
|
|
||||||
|
data class InterceptionPointModifier<out T : Any>(
|
||||||
|
val point: InterceptionPoint<@UnsafeVariance T>,
|
||||||
|
val modifier: (@UnsafeVariance T) -> T
|
||||||
|
)
|
||||||
+52
-26
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors
|
package org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.tools.projectWizard.Identificator
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.BuildFileIR
|
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.BuildFileIR
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.sourcesets
|
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.sourcesets
|
||||||
import org.jetbrains.kotlin.tools.projectWizard.templates.Template
|
import org.jetbrains.kotlin.tools.projectWizard.templates.Template
|
||||||
@@ -13,34 +14,60 @@ import org.jetbrains.kotlin.tools.projectWizard.transformers.TransformerFunction
|
|||||||
|
|
||||||
interface Interceptor
|
interface Interceptor
|
||||||
|
|
||||||
|
typealias InterceptionPointValues = Map<InterceptionPoint<Any>, Any>
|
||||||
|
typealias SourcesetInterceptionPointValues = Map<Identificator, InterceptionPointValues>
|
||||||
|
|
||||||
|
data class TemplateInterceptionApplicationState(
|
||||||
|
val buildFileIR: BuildFileIR,
|
||||||
|
val sourcesetToSettings: SourcesetInterceptionPointValues
|
||||||
|
)
|
||||||
|
|
||||||
data class TemplateInterceptor(
|
data class TemplateInterceptor(
|
||||||
|
val template: Template,
|
||||||
val applicabilityCheckers: List<Predicate<BuildFileIR>>,
|
val applicabilityCheckers: List<Predicate<BuildFileIR>>,
|
||||||
val buildFileTransformers: List<TransformerFunction<BuildFileIR>>
|
val buildFileTransformers: List<TransformerFunction<BuildFileIR>>,
|
||||||
|
val interceptionPointModifiers: List<InterceptionPointModifier<Any>>
|
||||||
) : Interceptor {
|
) : Interceptor {
|
||||||
fun applyTo(buildFileIR: BuildFileIR): BuildFileIR? {
|
fun applyTo(state: TemplateInterceptionApplicationState): TemplateInterceptionApplicationState {
|
||||||
if (applicabilityCheckers.any { checker -> !checker(buildFileIR) }) return null
|
if (applicabilityCheckers.any { checker -> !checker(state.buildFileIR) }) return state
|
||||||
|
|
||||||
var isApplied = false
|
val sourcesetsWithTemplate = state.buildFileIR.sourcesets.filter { it.template?.id == template.id }
|
||||||
var result: BuildFileIR = buildFileIR
|
if (sourcesetsWithTemplate.isEmpty()) return state
|
||||||
|
|
||||||
for (transformer in buildFileTransformers) {
|
val transformedBuildFile = applyBuildFileTransformers(state.buildFileIR)
|
||||||
val newResult = transformer(result)
|
|
||||||
if (newResult != null) {
|
val mutableValues = state.sourcesetToSettings.toMutableMap()
|
||||||
result = newResult
|
for (sourceset in sourcesetsWithTemplate) {
|
||||||
isApplied = true
|
mutableValues.compute(sourceset.original.identificator) { _, values ->
|
||||||
|
applyInterceptionPointModifiers(values.orEmpty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.takeIf { isApplied }
|
return TemplateInterceptionApplicationState(transformedBuildFile, mutableValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun applyUntilConverge(buildFileIR: BuildFileIR) =
|
private fun applyInterceptionPointModifiers(values: InterceptionPointValues): InterceptionPointValues {
|
||||||
generateSequence(buildFileIR, { buildFile ->
|
val mutableValues = values.toMutableMap()
|
||||||
applyTo(buildFile)
|
for (modifier in interceptionPointModifiers) {
|
||||||
}).last()
|
mutableValues.compute(modifier.point) { _, value ->
|
||||||
|
modifier.modifier(value ?: modifier.point.initialValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mutableValues
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyBuildFileTransformers(buildFile: BuildFileIR): BuildFileIR =
|
||||||
|
buildFileTransformers.fold(buildFile) { result, transformer ->
|
||||||
|
transformer(result) ?: result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TemplateInterceptorBuilder<T : Template>(private val template: T) {
|
fun List<TemplateInterceptor>.applyAll(state: TemplateInterceptionApplicationState) =
|
||||||
|
fold(state) { currentState, interceptor ->
|
||||||
|
interceptor.applyTo(currentState)
|
||||||
|
}
|
||||||
|
|
||||||
|
class TemplateInterceptorBuilder<T : Template>(val template: T) {
|
||||||
private val buildFileTransformers = mutableListOf<TransformerFunction<BuildFileIR>>()
|
private val buildFileTransformers = mutableListOf<TransformerFunction<BuildFileIR>>()
|
||||||
fun transformBuildFile(transform: TransformerFunction<BuildFileIR>) {
|
fun transformBuildFile(transform: TransformerFunction<BuildFileIR>) {
|
||||||
buildFileTransformers += transform
|
buildFileTransformers += transform
|
||||||
@@ -51,20 +78,19 @@ class TemplateInterceptorBuilder<T : Template>(private val template: T) {
|
|||||||
applicabilityCheckers += checker
|
applicabilityCheckers += checker
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
private val interceptionPointModifiers = mutableListOf<InterceptionPointModifier<Any>>()
|
||||||
applicableIf { buildFile ->
|
fun <T : Any> interceptAtPoint(point: InterceptionPoint<T>, modifier: (T) -> T) {
|
||||||
buildFile.sourcesets.any { it.template?.id == template.id }
|
interceptionPointModifiers.add(InterceptionPointModifier(point, modifier))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun build() = TemplateInterceptor(applicabilityCheckers, buildFileTransformers)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun List<TemplateInterceptor>.fold() =
|
fun build() = TemplateInterceptor(
|
||||||
TemplateInterceptor(
|
template,
|
||||||
flatMap { it.applicabilityCheckers },
|
applicabilityCheckers,
|
||||||
flatMap { it.buildFileTransformers }
|
buildFileTransformers,
|
||||||
|
interceptionPointModifiers
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun <T : Template> interceptTemplate(template: T, builder: TemplateInterceptorBuilder<T>.() -> Unit) =
|
fun <T : Template> interceptTemplate(template: T, builder: TemplateInterceptorBuilder<T>.() -> Unit) =
|
||||||
|
|||||||
Reference in New Issue
Block a user