Skip to content

Commit

Permalink
Automatically format generated files using ktlint if possible
Browse files Browse the repository at this point in the history
  • Loading branch information
Vampire committed Sep 23, 2024
1 parent 430420d commit 47fb400
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 1 deletion.
2 changes: 2 additions & 0 deletions gradle-plugin/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ kotlinVersion = 2.0.0
publishVersion = 1.2.1

jacksonVersion = 2.17.2
ktlintVersion = 1.3.1
ec4jVersion = 1.0.0

typescriptVersion = ^5.5.3
4 changes: 4 additions & 0 deletions gradle-plugin/karakum-gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ dependencies {
compileOnly(kotlin("gradle-plugin"))

implementation(libs.jackson.databind)
implementation(libs.ktlint.ruleEngine)
implementation(libs.ktlint.ruleEngine.core)
implementation(libs.ktlint.ruleset.standard)
implementation(libs.ec4j)

testImplementation(kotlin("test"))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
package io.github.sgrishchenko.karakum.gradle.plugin

import io.github.sgrishchenko.karakum.gradle.plugin.service.KtLintService
import io.github.sgrishchenko.karakum.gradle.plugin.tasks.KarakumConfig
import io.github.sgrishchenko.karakum.gradle.plugin.tasks.KarakumGenerate
import io.github.sgrishchenko.karakum.gradle.plugin.tasks.KarakumSync
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.FileTree
import org.gradle.api.services.BuildServiceRegistry
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.getValue
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.registerIfAbsent
import org.gradle.kotlin.dsl.registering
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
import javax.inject.Inject

abstract class KarakumPlugin : Plugin<Project> {
@get:Inject
abstract val sharedServices: BuildServiceRegistry

class KarakumPlugin : Plugin<Project> {
override fun apply(project: Project): Unit = with(project) {
plugins.withId("org.jetbrains.kotlin.multiplatform") {
val karakum = extensions.create<KarakumExtension>("karakum").apply {
Expand Down Expand Up @@ -51,6 +58,8 @@ class KarakumPlugin : Plugin<Project> {
destinationDirectory.convention(layout.dir(npmProjectDirectory).map { it.dir("karakum") })
}

sharedServices.registerIfAbsent("ktlintService", KtLintService::class)

val generateKarakumExternals by tasks.registering(KarakumGenerate::class) {
group = KARAKUM_GRADLE_PLUGIN_GROUP
description = "Generates the Kotlin external declarations using Karakum."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.github.sgrishchenko.karakum.gradle.plugin.service

import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine
import com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters.None

abstract class KtLintService : BuildService<None> {
val ktlint = KtLintRuleEngine(
ruleProviders = StandardRuleSetProvider().getRuleProviders()
).apply {
// ktlint stores .editorconfig files in a companion object and thus as static state
// this is not good when running multiple builds within the same Gradle daemon
// and the .editorconfig file might have changed, so clear that cache
// so that .editorconfig files are read freshly
trimMemory()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,27 @@ import com.fasterxml.jackson.databind.node.ArrayNode
import com.fasterxml.jackson.databind.node.ObjectNode
import io.github.sgrishchenko.karakum.gradle.plugin.karakumDependency
import io.github.sgrishchenko.karakum.gradle.plugin.kotlinJsCompilation
import io.github.sgrishchenko.karakum.gradle.plugin.service.KtLintService
import io.github.sgrishchenko.karakum.gradle.plugin.typescriptDependency
import io.github.sgrishchenko.karakum.gradle.plugin.worker.KtLintFormatWorker
import org.ec4j.core.Resource.Resources
import org.ec4j.core.ResourcePropertiesService
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.ProjectLayout
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.services.ServiceReference
import org.gradle.api.tasks.*
import org.gradle.kotlin.dsl.submit
import org.gradle.process.ExecOperations
import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation
import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
import java.nio.file.Path
import javax.inject.Inject
import kotlin.text.Charsets.UTF_8

abstract class KarakumGenerate : DefaultTask(), RequiresNpmDependencies {
@get:Inject
Expand All @@ -24,6 +34,12 @@ abstract class KarakumGenerate : DefaultTask(), RequiresNpmDependencies {
@get:Inject
abstract val layout: ProjectLayout

@get:Inject
abstract val workerExecutor: WorkerExecutor

@get:ServiceReference
abstract val ktlintService: Property<KtLintService>

@get:InputFile
abstract val configFile: RegularFileProperty

Expand Down Expand Up @@ -71,6 +87,18 @@ abstract class KarakumGenerate : DefaultTask(), RequiresNpmDependencies {
}
}

@get:InputFiles
@get:Optional
val editorConfigFiles = destinationDirectory.map { destinationDirectory ->
ResourcePropertiesService
.builder()
.build()
.queryProperties(Resources.ofPath(destinationDirectory.asFile.absoluteFile.toPath(), UTF_8))
.editorConfigFiles
.map { it.getAdapter(Path::class.java) }
.let { layout.files(it) }
}

@TaskAction
fun generate() {
exec.exec {
Expand All @@ -80,5 +108,12 @@ abstract class KarakumGenerate : DefaultTask(), RequiresNpmDependencies {
args = listOf("--config", configFile.get().asFile.absolutePath)
)
}

logger.lifecycle("Formatting generated files")
destinationDirectory.get().asFileTree.forEach { file ->
workerExecutor.noIsolation().submit(KtLintFormatWorker::class) {
this.file.set(file)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.github.sgrishchenko.karakum.gradle.plugin.worker

import com.pinterest.ktlint.rule.engine.api.Code
import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.ALLOW_AUTOCORRECT
import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.NO_AUTOCORRECT
import io.github.sgrishchenko.karakum.gradle.plugin.service.KtLintService
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Property
import org.gradle.api.services.ServiceReference
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters

abstract class KtLintFormatWorker : WorkAction<KtLintFormatWorker.Parameters> {
private val logger = Logging.getLogger(KtLintFormatWorker::class.java)

@get:ServiceReference
abstract val ktlintService: Property<KtLintService>

override fun execute() {
val file = parameters.file.get().asFile
logger.info("Formatting ${file.absolutePath}")
runCatching {
ktlintService
.get()
.ktlint
.format(Code.fromFile(file)) { lintError ->
if (lintError.canBeAutoCorrected) ALLOW_AUTOCORRECT else NO_AUTOCORRECT
}.also(file::writeText)
}.onFailure {
logger.info("Formatting failed for ${file.absolutePath}")
logger.info(it.stackTraceToString())
}.onSuccess {
logger.info("Formatting successful for ${file.absolutePath}")
}
}

interface Parameters : WorkParameters {
val file: RegularFileProperty
}
}
6 changes: 6 additions & 0 deletions gradle-plugin/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ dependencyResolutionManagement {
val libs by creating {
val jacksonVersion: String by settings
library("jackson-databind", "com.fasterxml.jackson.core", "jackson-databind").version(jacksonVersion)
val ktlintVersion: String by settings
library("ktlint-ruleEngine", "com.pinterest.ktlint", "ktlint-rule-engine").version(ktlintVersion)
library("ktlint-ruleEngine-core", "com.pinterest.ktlint", "ktlint-rule-engine-core").version(ktlintVersion)
library("ktlint-ruleset-standard", "com.pinterest.ktlint", "ktlint-ruleset-standard").version(ktlintVersion)
val ec4jVersion: String by settings
library("ec4j", "org.ec4j.core", "ec4j-core").version(ec4jVersion)
}
}
}
Expand Down

0 comments on commit 47fb400

Please sign in to comment.