mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
refactor(jetbrains): move buildSrc to build-tasks composite build
Extract all custom Gradle task classes (FixGeneratedApiTask, PrepareLocalCliTask, CheckCliTask) into a build-tasks/ composite build exposed via includeBuild. This keeps backend/build.gradle.kts purely declarative and co-locates build logic in its own compilable module.
This commit is contained in:
@@ -3,6 +3,7 @@ plugins {
|
||||
alias(libs.plugins.kotlin)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.openapi.generator)
|
||||
id("ai.kilocode.jetbrains.build-tasks")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@@ -18,6 +19,8 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
// ── OpenAPI client generation ───────────────────────────────────────
|
||||
|
||||
openApiGenerate {
|
||||
generatorName.set("kotlin")
|
||||
library.set("jvm-okhttp4")
|
||||
@@ -34,18 +37,15 @@ openApiGenerate {
|
||||
"sourceFolder" to "src/main/kotlin",
|
||||
"enumPropertyNaming" to "UPPERCASE",
|
||||
))
|
||||
// Remap schema "File" so the generated class is not named java.io.File
|
||||
modelNameMappings.set(mapOf(
|
||||
"File" to "DiffFileInfo",
|
||||
))
|
||||
// Map empty anyOf references to kotlin.Any; bare numbers to Double
|
||||
typeMappings.set(mapOf(
|
||||
"AnyOfLessThanGreaterThan" to "kotlin.Any",
|
||||
"anyOf<>" to "kotlin.Any",
|
||||
"number" to "kotlin.Double",
|
||||
"decimal" to "kotlin.Double",
|
||||
))
|
||||
// Normalise OpenAPI 3.1 → 3.0-compatible patterns
|
||||
openapiNormalizer.set(mapOf(
|
||||
"SIMPLIFY_ANYOF_STRING_AND_ENUM_STRING" to "true",
|
||||
"SIMPLIFY_ONEOF_ANYOF" to "true",
|
||||
@@ -56,202 +56,17 @@ openApiGenerate {
|
||||
generateModelDocumentation.set(false)
|
||||
}
|
||||
|
||||
// Fix openapi-generator codegen bugs in generated Kotlin sources.
|
||||
//
|
||||
// 1) Boolean const enum fix:
|
||||
// `const: true`/`const: false` fields produce broken single-value enums.
|
||||
// Fix: replace with kotlin.Boolean, remove the enum class.
|
||||
//
|
||||
// 2) Double-parentheses on HashMap-extending data classes:
|
||||
// `data class Foo(...) : HashMap<String, Any>()()` — extra `()`.
|
||||
// Fix: remove the trailing `()`.
|
||||
//
|
||||
// 7) Empty anyOf wrapper classes:
|
||||
// anyOf unions of heterogeneous types (e.g. string enum | object) generate
|
||||
// empty `class Foo () {}` that can't deserialize primitives.
|
||||
// Fix: replace references with JsonElement, delete the empty class files.
|
||||
//
|
||||
// 3) Private Double constructor:
|
||||
// `kotlin.Double("5000")` — Double has no public String constructor.
|
||||
// Fix: convert to `5000.0` double literal.
|
||||
//
|
||||
// 4) Missing @Contextual on bare kotlin.Any fields:
|
||||
// kotlinx.serialization can't serialize `Any` without @Contextual.
|
||||
// Fix: add @Contextual annotation where missing.
|
||||
//
|
||||
// 5) Nullable body access in ApiClient.kt:
|
||||
// `response.body` is nullable in OkHttp but generated code dereferences
|
||||
// it without safe calls. Fix: replace `body.` with `body?.`.
|
||||
val fixGeneratedApi by tasks.registering {
|
||||
val fixGeneratedApi by tasks.registering(FixGeneratedApiTask::class) {
|
||||
dependsOn("openApiGenerate")
|
||||
val dir = generatedApi
|
||||
doLast {
|
||||
// ── Fix 7: empty anyOf wrapper classes → JsonElement ────────
|
||||
// These are anyOf unions (e.g. string enum | object) that the
|
||||
// codegen produces as empty classes. Replace all references with
|
||||
// kotlinx.serialization.json.JsonElement and delete the files.
|
||||
val modelDir = dir.get().file("ai/kilocode/jetbrains/api/model").asFile
|
||||
val emptyWrappers = modelDir.listFiles()
|
||||
?.filter { it.extension == "kt" }
|
||||
?.filter { f ->
|
||||
val text = f.readText()
|
||||
// Match: non-data `class Foo ()` with no `val` properties
|
||||
text.contains(Regex("""\nclass \w+ \(\n\n\)""")) && !text.contains("val ")
|
||||
}
|
||||
?.map { it.nameWithoutExtension }
|
||||
?: emptyList()
|
||||
|
||||
if (emptyWrappers.isNotEmpty()) {
|
||||
// Delete the empty wrapper class files
|
||||
for (name in emptyWrappers) {
|
||||
val f = File(modelDir, "$name.kt")
|
||||
if (f.exists()) f.delete()
|
||||
}
|
||||
// Replace references in all generated files
|
||||
dir.get().asFile.walkTopDown().filter { it.extension == "kt" }.forEach { file ->
|
||||
var text = file.readText()
|
||||
var changed = false
|
||||
for (name in emptyWrappers) {
|
||||
if (!text.contains(name)) continue
|
||||
// Remove import lines FIRST (before replacing class names)
|
||||
text = text.replace(Regex("""import [^\n]*\.$name\n"""), "")
|
||||
// Replace type references in code
|
||||
text = text.replace(Regex("""\b$name\b"""), "kotlinx.serialization.json.JsonElement")
|
||||
changed = true
|
||||
}
|
||||
if (changed) file.writeText(text)
|
||||
}
|
||||
}
|
||||
|
||||
dir.get().asFile.walkTopDown().filter { it.extension == "kt" }.forEach { file ->
|
||||
var text = file.readText()
|
||||
var changed = false
|
||||
|
||||
// ── Fix 1: boolean const enums ──────────────────────────
|
||||
val enumDecl = Regex(
|
||||
"""enum class (\w+)\(val value: kotlin\.Boolean\)"""
|
||||
)
|
||||
val names = enumDecl.findAll(text).map { it.groupValues[1] }.toList()
|
||||
for (name in names) {
|
||||
text = text.replace(Regex("""(val \w+:\s*)\w+\.$name""")) { m ->
|
||||
"${m.groupValues[1]}kotlin.Boolean"
|
||||
}
|
||||
text = text.replace(Regex(
|
||||
"""\n\s*@Serializable\s*\n\s*enum class $name\(val value: kotlin\.Boolean\)\s*\{[^}]*\}"""
|
||||
), "")
|
||||
text = text.replace(Regex(
|
||||
"""\n\s*/\*\*\s*\n(\s*\*[^\n]*\n)*\s*\*/\s*(?=\n\s*\n)"""
|
||||
), "")
|
||||
changed = true
|
||||
}
|
||||
|
||||
// ── Fix 2: double-parentheses `HashMap<...>()()` ────────
|
||||
if (text.contains("()()")) {
|
||||
text = text.replace("()()", "()")
|
||||
changed = true
|
||||
}
|
||||
|
||||
// ── Fix 3: `kotlin.Double("...")` → double literal ──────
|
||||
val doubleCtorPattern = Regex("""kotlin\.Double\("(\d+(?:\.\d+)?)"\)""")
|
||||
if (doubleCtorPattern.containsMatchIn(text)) {
|
||||
text = doubleCtorPattern.replace(text) { m ->
|
||||
val num = m.groupValues[1]
|
||||
if (num.contains(".")) num else "$num.0"
|
||||
}
|
||||
changed = true
|
||||
}
|
||||
|
||||
// ── Fix 4: add @Contextual to bare `kotlin.Any` usages ──
|
||||
// kotlinx.serialization cannot handle kotlin.Any without @Contextual.
|
||||
// Only patch @Serializable data class files (which import Contextual).
|
||||
// Skip enum files, API client files, and infrastructure.
|
||||
if (text.contains("kotlin.Any") &&
|
||||
text.contains("import kotlinx.serialization.Contextual") &&
|
||||
text.contains("@Serializable") &&
|
||||
text.contains("data class")
|
||||
) {
|
||||
// Add @Contextual before kotlin.Any in val/field type positions
|
||||
// Covers: `val foo: kotlin.Any`, `Map<String, kotlin.Any>`, etc.
|
||||
text = text.replace(
|
||||
Regex("""(?<!@Contextual )kotlin\.Any"""),
|
||||
"@Contextual kotlin.Any"
|
||||
)
|
||||
changed = true
|
||||
}
|
||||
|
||||
// ── Fix 5: nullable body in ApiClient ───────────────────
|
||||
// OkHttp's response.body is nullable but the generated code
|
||||
// dereferences it without null checks in two places:
|
||||
// a) responseBody() — add null guard so body smart-casts
|
||||
// b) request() error branches — `it.body.string()` → `it.body?.string()`
|
||||
if (file.name == "ApiClient.kt") {
|
||||
val guard = "val body = response.body"
|
||||
if (text.contains(guard) && !text.contains("if (body == null) return null")) {
|
||||
text = text.replace(
|
||||
guard,
|
||||
"$guard\n if (body == null) return null"
|
||||
)
|
||||
// After the null guard, remove safe calls that cause
|
||||
// InputStream? issues (body is smart-cast non-null).
|
||||
text = text.replace("body?.", "body.")
|
||||
changed = true
|
||||
}
|
||||
// Fix `it.body.string()` in error branches of request()
|
||||
if (text.contains("it.body.string()")) {
|
||||
text = text.replace("it.body.string()", "it.body?.string()")
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
// ── Fix 6: register AnySerializer in Serializer.kt ──────
|
||||
// kotlinx.serialization needs a contextual serializer for Any
|
||||
// that delegates to JsonElement for dynamic JSON values.
|
||||
if (file.name == "Serializer.kt") {
|
||||
if (!text.contains("AnySerializer")) {
|
||||
// Add import + serializer object at end of file
|
||||
text = text.replace(
|
||||
"import kotlinx.serialization.modules.SerializersModuleBuilder",
|
||||
"import kotlinx.serialization.modules.SerializersModuleBuilder\n" +
|
||||
"import kotlinx.serialization.KSerializer\n" +
|
||||
"import kotlinx.serialization.descriptors.SerialDescriptor\n" +
|
||||
"import kotlinx.serialization.encoding.Decoder\n" +
|
||||
"import kotlinx.serialization.encoding.Encoder\n" +
|
||||
"import kotlinx.serialization.json.JsonDecoder\n" +
|
||||
"import kotlinx.serialization.json.JsonEncoder\n" +
|
||||
"import kotlinx.serialization.json.JsonElement"
|
||||
)
|
||||
// Register it in the SerializersModule
|
||||
text = text.replace(
|
||||
"contextual(StringBuilder::class, StringBuilderAdapter)",
|
||||
"contextual(StringBuilder::class, StringBuilderAdapter)\n" +
|
||||
" contextual(Any::class, AnySerializer)"
|
||||
)
|
||||
// Append the serializer object before the closing of Serializer
|
||||
text = text.trimEnd() + "\n\n" +
|
||||
"internal object AnySerializer : KSerializer<Any> {\n" +
|
||||
" private val delegate = JsonElement.serializer()\n" +
|
||||
" override val descriptor: SerialDescriptor = delegate.descriptor\n" +
|
||||
" override fun serialize(encoder: Encoder, value: Any) {\n" +
|
||||
" val json = (encoder as JsonEncoder).json\n" +
|
||||
" encoder.encodeSerializableValue(delegate, json.encodeToJsonElement(delegate, value as? JsonElement ?: return))\n" +
|
||||
" }\n" +
|
||||
" override fun deserialize(decoder: Decoder): Any {\n" +
|
||||
" return (decoder as JsonDecoder).decodeJsonElement()\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) file.writeText(text)
|
||||
}
|
||||
}
|
||||
generated.set(generatedApi)
|
||||
}
|
||||
|
||||
tasks.named("compileKotlin") {
|
||||
dependsOn(fixGeneratedApi)
|
||||
}
|
||||
|
||||
// ── CLI binary packaging ────────────────────────────────────────────
|
||||
|
||||
val cliDir = layout.buildDirectory.dir("generated/cli/cli")
|
||||
val production = providers.gradleProperty("production").map { it.toBoolean() }.orElse(false)
|
||||
|
||||
@@ -286,43 +101,23 @@ val localCli by tasks.registering(PrepareLocalCliTask::class) {
|
||||
exe.set(platform.map { if (it.startsWith("windows")) "kilo.exe" else "kilo" })
|
||||
}
|
||||
|
||||
val checkCli by tasks.registering {
|
||||
val prod = production
|
||||
val checkCli by tasks.registering(CheckCliTask::class) {
|
||||
description = "Verify CLI binaries exist before building"
|
||||
val dir = cliDir.map { it.asFile }
|
||||
val prod = production.get()
|
||||
val platforms = requiredPlatforms.toList()
|
||||
if (!prod) {
|
||||
dir.set(cliDir)
|
||||
this.production.set(prod)
|
||||
platforms.set(requiredPlatforms)
|
||||
if (!prod.get()) {
|
||||
dependsOn(localCli)
|
||||
}
|
||||
doLast {
|
||||
val resolved = dir.get()
|
||||
if (!resolved.exists() || resolved.listFiles()?.isEmpty() != false) {
|
||||
throw GradleException(
|
||||
"CLI binaries not found at ${resolved.absolutePath}.\n" +
|
||||
"Run 'bun run build' from packages/kilo-jetbrains/ to build CLI and plugin together."
|
||||
)
|
||||
}
|
||||
if (prod) {
|
||||
val missing = platforms.filter { platform ->
|
||||
val dir = File(resolved, platform)
|
||||
val exe = if (platform.startsWith("windows")) "kilo.exe" else "kilo"
|
||||
!File(dir, exe).exists()
|
||||
}
|
||||
if (missing.isNotEmpty()) {
|
||||
throw GradleException(
|
||||
"Production build requires all platform CLI binaries.\n" +
|
||||
"Missing: ${missing.joinToString(", ")}\n" +
|
||||
"Run 'bun run build:production' to build all platforms."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
dependsOn(checkCli)
|
||||
}
|
||||
|
||||
// ── Dependencies ────────────────────────────────────────────────────
|
||||
|
||||
dependencies {
|
||||
intellijPlatform {
|
||||
intellijIdea(libs.versions.intellij.platform)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("build-tasks") {
|
||||
id = "ai.kilocode.jetbrains.build-tasks"
|
||||
implementationClass = "BuildTasksPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
rootProject.name = "build-tasks"
|
||||
@@ -0,0 +1,13 @@
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
/**
|
||||
* Empty marker plugin that exposes task classes from this build-logic
|
||||
* module to the main build. Apply it in any subproject that needs
|
||||
* [FixGeneratedApiTask], [PrepareLocalCliTask], or [CheckCliTask].
|
||||
*/
|
||||
class BuildTasksPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
// Task classes are available on the classpath once this plugin is applied.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Verify that CLI binaries exist before packaging the plugin.
|
||||
* In production mode, all platform binaries must be present.
|
||||
* In dev mode, only the current platform binary is required.
|
||||
*/
|
||||
abstract class CheckCliTask : DefaultTask() {
|
||||
@get:InputDirectory
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract val dir: DirectoryProperty
|
||||
|
||||
@get:Input
|
||||
abstract val production: Property<Boolean>
|
||||
|
||||
@get:Input
|
||||
abstract val platforms: ListProperty<String>
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val resolved = dir.get().asFile
|
||||
if (!resolved.exists() || resolved.listFiles()?.isEmpty() != false) {
|
||||
throw GradleException(
|
||||
"CLI binaries not found at ${resolved.absolutePath}.\n" +
|
||||
"Run 'bun run build' from packages/kilo-jetbrains/ to build CLI and plugin together."
|
||||
)
|
||||
}
|
||||
if (production.get()) {
|
||||
val missing = platforms.get().filter { platform ->
|
||||
val d = File(resolved, platform)
|
||||
val exe = if (platform.startsWith("windows")) "kilo.exe" else "kilo"
|
||||
!File(d, exe).exists()
|
||||
}
|
||||
if (missing.isNotEmpty()) {
|
||||
throw GradleException(
|
||||
"Production build requires all platform CLI binaries.\n" +
|
||||
"Missing: ${missing.joinToString(", ")}\n" +
|
||||
"Run 'bun run build:production' to build all platforms."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Post-process openapi-generator output to fix codegen bugs that produce
|
||||
* uncompilable or runtime-broken Kotlin when using kotlinx.serialization.
|
||||
*
|
||||
* Fixes applied:
|
||||
* 1. Boolean const enums — `const: true`/`false` produce broken single-value
|
||||
* enums. Replaced with plain `kotlin.Boolean`.
|
||||
* 2. Double parentheses — `HashMap<…>()()` trailing extra `()`.
|
||||
* 3. Private Double constructor — `kotlin.Double("5000")` → `5000.0`.
|
||||
* 4. Missing @Contextual on `kotlin.Any` — kotlinx.serialization can't
|
||||
* serialize `Any` without it.
|
||||
* 5. Nullable body in ApiClient — OkHttp's `response.body` is nullable.
|
||||
* 6. AnySerializer — registers a contextual `KSerializer<Any>` backed by
|
||||
* `JsonElement` for dynamic JSON values.
|
||||
* 7. Empty anyOf wrappers — `anyOf` unions that generate empty classes.
|
||||
* Replaced with `kotlinx.serialization.json.JsonElement`.
|
||||
*/
|
||||
abstract class FixGeneratedApiTask : DefaultTask() {
|
||||
@get:InputDirectory
|
||||
abstract val generated: DirectoryProperty
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val root = generated.get().asFile
|
||||
fixEmptyWrappers(root)
|
||||
root.walkTopDown().filter { it.extension == "kt" }.forEach { fix(it) }
|
||||
}
|
||||
|
||||
// ── Fix 7: empty anyOf wrapper classes → JsonElement ─────────────
|
||||
private fun fixEmptyWrappers(root: File) {
|
||||
val models = File(root, "ai/kilocode/jetbrains/api/model")
|
||||
if (!models.isDirectory) return
|
||||
|
||||
val empty = Regex("""\nclass \w+ \(\n\n\)""")
|
||||
val wrappers = models.listFiles()
|
||||
?.filter { it.extension == "kt" }
|
||||
?.filter { f -> val t = f.readText(); empty.containsMatchIn(t) && !t.contains("val ") }
|
||||
?.map { it.nameWithoutExtension }
|
||||
?: return
|
||||
|
||||
for (name in wrappers) File(models, "$name.kt").delete()
|
||||
|
||||
root.walkTopDown().filter { it.extension == "kt" }.forEach { file ->
|
||||
var text = file.readText()
|
||||
var changed = false
|
||||
for (name in wrappers) {
|
||||
if (!text.contains(name)) continue
|
||||
text = text.replace(Regex("""import [^\n]*\.$name\n"""), "")
|
||||
text = text.replace(Regex("""\b$name\b"""), "kotlinx.serialization.json.JsonElement")
|
||||
changed = true
|
||||
}
|
||||
if (changed) file.writeText(text)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fix(file: File) {
|
||||
var text = file.readText()
|
||||
var changed = false
|
||||
|
||||
// ── Fix 1: boolean const enums ──────────────────────────────
|
||||
val decl = Regex("""enum class (\w+)\(val value: kotlin\.Boolean\)""")
|
||||
for (name in decl.findAll(text).map { it.groupValues[1] }.toList()) {
|
||||
text = text.replace(Regex("""(val \w+:\s*)\w+\.$name""")) { m ->
|
||||
"${m.groupValues[1]}kotlin.Boolean"
|
||||
}
|
||||
text = text.replace(Regex(
|
||||
"""\n\s*@Serializable\s*\n\s*enum class $name\(val value: kotlin\.Boolean\)\s*\{[^}]*\}"""
|
||||
), "")
|
||||
text = text.replace(Regex(
|
||||
"""\n\s*/\*\*\s*\n(\s*\*[^\n]*\n)*\s*\*/\s*(?=\n\s*\n)"""
|
||||
), "")
|
||||
changed = true
|
||||
}
|
||||
|
||||
// ── Fix 2: double parentheses `HashMap<…>()()` ─────────────
|
||||
if (text.contains("()()")) {
|
||||
text = text.replace("()()", "()")
|
||||
changed = true
|
||||
}
|
||||
|
||||
// ── Fix 3: `kotlin.Double("…")` → double literal ───────────
|
||||
val ctor = Regex("""kotlin\.Double\("(\d+(?:\.\d+)?)"\)""")
|
||||
if (ctor.containsMatchIn(text)) {
|
||||
text = ctor.replace(text) { m ->
|
||||
val n = m.groupValues[1]
|
||||
if (n.contains(".")) n else "$n.0"
|
||||
}
|
||||
changed = true
|
||||
}
|
||||
|
||||
// ── Fix 4: @Contextual on bare kotlin.Any ───────────────────
|
||||
if (text.contains("kotlin.Any") &&
|
||||
text.contains("import kotlinx.serialization.Contextual") &&
|
||||
text.contains("@Serializable") &&
|
||||
text.contains("data class")
|
||||
) {
|
||||
text = text.replace(
|
||||
Regex("""(?<!@Contextual )kotlin\.Any"""),
|
||||
"@Contextual kotlin.Any"
|
||||
)
|
||||
changed = true
|
||||
}
|
||||
|
||||
// ── Fix 5: nullable body in ApiClient ───────────────────────
|
||||
if (file.name == "ApiClient.kt") {
|
||||
val guard = "val body = response.body"
|
||||
if (text.contains(guard) && !text.contains("if (body == null) return null")) {
|
||||
text = text.replace(guard, "$guard\n if (body == null) return null")
|
||||
text = text.replace("body?.", "body.")
|
||||
changed = true
|
||||
}
|
||||
if (text.contains("it.body.string()")) {
|
||||
text = text.replace("it.body.string()", "it.body?.string()")
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
// ── Fix 6: AnySerializer in Serializer.kt ───────────────────
|
||||
if (file.name == "Serializer.kt" && !text.contains("AnySerializer")) {
|
||||
text = text.replace(
|
||||
"import kotlinx.serialization.modules.SerializersModuleBuilder",
|
||||
"import kotlinx.serialization.modules.SerializersModuleBuilder\n" +
|
||||
"import kotlinx.serialization.KSerializer\n" +
|
||||
"import kotlinx.serialization.descriptors.SerialDescriptor\n" +
|
||||
"import kotlinx.serialization.encoding.Decoder\n" +
|
||||
"import kotlinx.serialization.encoding.Encoder\n" +
|
||||
"import kotlinx.serialization.json.JsonDecoder\n" +
|
||||
"import kotlinx.serialization.json.JsonEncoder\n" +
|
||||
"import kotlinx.serialization.json.JsonElement"
|
||||
)
|
||||
text = text.replace(
|
||||
"contextual(StringBuilder::class, StringBuilderAdapter)",
|
||||
"contextual(StringBuilder::class, StringBuilderAdapter)\n" +
|
||||
" contextual(Any::class, AnySerializer)"
|
||||
)
|
||||
text = text.trimEnd() + "\n\n" +
|
||||
"internal object AnySerializer : KSerializer<Any> {\n" +
|
||||
" private val delegate = JsonElement.serializer()\n" +
|
||||
" override val descriptor: SerialDescriptor = delegate.descriptor\n" +
|
||||
" override fun serialize(encoder: Encoder, value: Any) {\n" +
|
||||
" val json = (encoder as JsonEncoder).json\n" +
|
||||
" encoder.encodeSerializableValue(delegate, json.encodeToJsonElement(delegate, value as? JsonElement ?: return))\n" +
|
||||
" }\n" +
|
||||
" override fun deserialize(decoder: Decoder): Any {\n" +
|
||||
" return (decoder as JsonDecoder).decodeJsonElement()\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
changed = true
|
||||
}
|
||||
|
||||
if (changed) file.writeText(text)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
@@ -5,6 +5,7 @@ include("frontend")
|
||||
include("backend")
|
||||
|
||||
pluginManagement {
|
||||
includeBuild("build-tasks")
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
|
||||
Reference in New Issue
Block a user