Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
e9aa683
chore: Add initial Test class with main method for demonstration
LMLiam Aug 28, 2025
c93e08c
Merge branch 'master' into feature/issue-1/initial-dsl-setup
LMLiam Aug 28, 2025
b1766ce
chore: Remove JUnit platform configuration from build.gradle and upda…
LMLiam Aug 28, 2025
b7384b4
feat: Add initial DSL setup with textComponent function and correspon…
LMLiam Aug 28, 2025
fcc6385
feat: Add kotest-assertions dependency for enhanced testing capabilities
LMLiam Aug 28, 2025
bc6f993
chore: Remove initial Test.kt
LMLiam Aug 28, 2025
e02e179
feat: Add GitHub Actions workflow for building and running Kotest tests
LMLiam Aug 28, 2025
7d92d43
feat: Add test for textComponent function to verify builder instance
LMLiam Aug 28, 2025
9a8fa2e
feat: Update GitHub Actions workflow to trigger on master branch
LMLiam Aug 28, 2025
a3b4938
feat: Update GitHub Actions workflow to fetch full history
LMLiam Aug 28, 2025
0e7a616
feat: Update Java version in GitHub Actions workflow to 24
LMLiam Aug 28, 2025
6a8f917
feat: Upgrade JVM toolchain to version 24 and enhance GitHub Actions …
LMLiam Aug 28, 2025
4b8897a
feat: Add assertion to verify TextComponent instance in ComponentDslTest
LMLiam Aug 28, 2025
2fc31c0
feat: Include passed tests in summary for GitHub Actions workflow
LMLiam Aug 28, 2025
83d4e93
feat: Update project version to 0.0.1-PRE-ALPHA and modify test repor…
LMLiam Aug 28, 2025
6c5b7c7
feat: Configure Kotest with JUnit XML reporting and update test depen…
LMLiam Aug 28, 2025
0b4bcf5
feat: Remove unnecessary annotation notice from GitHub Actions workflow
LMLiam Aug 28, 2025
4b7a200
feat: Remove JUnit XML reporting configuration and unused Kotest depe…
LMLiam Aug 28, 2025
78cc90c
feat: Add spotlessCheck dependency to check task
LMLiam Aug 30, 2025
10ede33
feat: Implement TextComponentScope DSL for building text components
LMLiam Aug 30, 2025
4dc2852
feat: Disable test task in build configuration
LMLiam Aug 30, 2025
a7df750
feat: Rename TextComponentScope to DefaultTextComponentScope and intr…
LMLiam Aug 30, 2025
63a3a0f
feat: Update check task to depend on spotlessApply and enhance TextCo…
LMLiam Aug 30, 2025
c8df29e
feat: Add code quality check step using spotlessCheck in build process
LMLiam Aug 30, 2025
21503c0
feat: Set up initial DSL structure for TextComponentScope and core-ap…
LMLiam Aug 30, 2025
71aafda
feat: Refactor TextComponentScope factory loading logic for clarity
LMLiam Aug 30, 2025
98c90d2
feat: Implement service file generation and introduce ServiceContract…
LMLiam Aug 30, 2025
4b505d9
feat: Add task to generate service files for @ServiceContract and @Se…
LMLiam Aug 30, 2025
779e4ed
feat: Add initial setup for e2e testing and update dependencies
LMLiam Aug 30, 2025
86902c5
feat: Enhance ServiceLoaderE2ETest with factory component assertions …
LMLiam Aug 30, 2025
9a0af72
feat: Remove service file generation task and update dependencies for…
LMLiam Aug 30, 2025
ac48e21
feat: Update DSL setup with Kotlin KAPT and rename TextComponentScope…
LMLiam Aug 30, 2025
40f8317
feat: Update package structure and dependencies for DSL setup
LMLiam Aug 30, 2025
9e9812e
feat: Update dependencies for spi-tooling annotations and processor
LMLiam Aug 30, 2025
74cf67d
feat: Update spi-tooling annotations and processor dependencies to ve…
LMLiam Aug 31, 2025
c15138e
feat: Update build configuration and dependencies for improved DSL setup
LMLiam Aug 31, 2025
3d66849
feat: Remove versions.gradle dependency and add Sonatype snapshots re…
LMLiam Aug 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Build & Kotest
on:
push:
branches:
- master
pull_request:
types:
- opened
- synchronize

permissions:
packages: read

jobs:
build:
runs-on: ubuntu-latest
env:
ORG_GRADLE_PROJECT_gprUse: 'true'
ORG_GRADLE_PROJECT_gprUser: ${{ github.actor }}
ORG_GRADLE_PROJECT_gprToken: ${{ secrets.GITHUB_TOKEN }}
permissions:
checks: write
pull-requests: write
steps:
- name: Checkout Repository
uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Set up JDK
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '24'
cache: 'gradle'

- name: Executable Gradle
run: chmod +x gradlew

- name: Check Code Quality
run: ./gradlew spotlessCheck

- name: Build with Gradle
run: ./gradlew build

- name: Run Kotest tests
run: ./gradlew kotest

- name: Publish Test Report
uses: mikepenz/action-junit-report@v5
with:
check_name: 'Test Report'
report_paths: '**/jvmKotest/TEST-*.xml'
fail_on_failure: 'true'
require_tests: 'true'
detailed_summary: 'true'
flaky_summary: 'true'
include_time_in_summary: 'true'
comment: 'true'
updateComment: 'true'
include_passed: 'true'


18 changes: 11 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ plugins {
id 'idea'
}

group = 'org.eventhorizonlab.kyoriadventuredsl'
version = '1.0-SNAPSHOT'
group = 'com.github.eventhorizonlab'
version = '0.0.1-PRE-ALPHA'

subprojects {
apply plugin: 'org.jetbrains.kotlin.jvm'
Expand All @@ -16,11 +16,7 @@ subprojects {
apply plugin: 'org.jlleitschuh.gradle.ktlint'

kotlin {
jvmToolchain(23)
}

test {
useJUnitPlatform()
jvmToolchain(24)
}

apply from: rootProject.file('gradle/repositories.gradle')
Expand All @@ -33,6 +29,14 @@ subprojects {
destinationDirectory.set(rootProject.layout.buildDirectory.dir("libs"))
}

tasks.named("check") {
dependsOn("spotlessApply")
}

tasks.named("test") {
enabled = false
}

apply from: rootProject.file('gradle/spotless.gradle')
}

Expand Down
8 changes: 8 additions & 0 deletions e2e/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
dependencies {
testImplementation(project(":core-api"))
testImplementation(files(project(":core").tasks.named("jar").map { it.archiveFile }))
}

tasks.named("kotest") {
dependsOn(":core:jar")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.github.eventhorizonlab.kyoriadventuredsl.api

import com.github.eventhorizonlab.core.api.TextComponentScope
import com.github.eventhorizonlab.core.api.textComponent
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.instanceOf
import net.kyori.adventure.text.format.NamedTextColor
import java.util.*

class ServiceLoaderE2ETest :
StringSpec({
"ServiceLoader should discover and use TextComponentScope.Factory" {
val loader =
ServiceLoader.load(
TextComponentScope.Factory::class.java,
Thread.currentThread().contextClassLoader
)
val factories = loader.toList()

factories.size shouldBe 1

val factory = factories.first()

factory shouldBe instanceOf<TextComponentScope.Factory>()
factory.javaClass.classLoader shouldBe TextComponentScope::class.java.classLoader
factory.javaClass.isInterface shouldBe false

val factoryComponent =
factory.create {
content("Hello E2E")
color(NamedTextColor.RED)
}

factoryComponent.content() shouldBe "Hello E2E"
factoryComponent.color() shouldBe NamedTextColor.RED

val component =
textComponent {
content("Hello E2E")
color(NamedTextColor.RED)
}

component.content() shouldBe "Hello E2E"
component.color() shouldBe NamedTextColor.RED
}
})
6 changes: 4 additions & 2 deletions gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ configurations {
}

def dep = [
"adventure-api": "net.kyori:adventure-api:${versions.'adventure-api'}",
"kotest" : "io.kotest:kotest-framework-engine:${versions.kotest}"
"adventure-api" : "net.kyori:adventure-api:${versions.'adventure-api'}",
"kotest" : "io.kotest:kotest-framework-engine:${versions.kotest}",
"kotest-assertions" : "io.kotest:kotest-assertions-core:${versions.kotest}"
]

dependencies {
implementation dep."adventure-api"

testImplementation dep.kotest
testImplementation dep."kotest-assertions"
}
13 changes: 13 additions & 0 deletions gradle/repositories.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
repositories {
mavenLocal()
mavenCentral()
maven {
url = uri("https://central.sonatype.com/repository/maven-snapshots/")
}
if (project.findProperty("gprUse") != null) {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/EventHorizonLab/SPI-Tooling")
credentials {
username = project.findProperty("gprUser") as String
password = project.findProperty("gprToken") as String
}
}
}
}
2 changes: 1 addition & 1 deletion gradle/spotless.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ spotless {
target '*.gradle', '.gitattributes', '.gitignore'

trimTrailingWhitespace()
indentWithSpaces(4)
leadingTabsToSpaces()
endWithNewline()
}

Expand Down
3 changes: 2 additions & 1 deletion gradle/versions.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"kotest": "6.0.1",
"adventure-api": "4.24.0"
"adventure-api": "4.24.0",
"spi": "0.1.17"
}
3 changes: 3 additions & 0 deletions modules/core-api/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies {
compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:${versions.spi}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.github.eventhorizonlab.core.api

import com.github.eventhorizonlab.spi.ServiceContract
import net.kyori.adventure.text.TextComponent
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextDecoration
import java.util.*

@DslMarker
internal annotation class AdventureDsl

@AdventureDsl
interface TextComponentScope {
fun content(content: String)

fun color(color: NamedTextColor)

fun decorate(vararg decorations: TextDecoration)

fun text(init: TextComponentScope.() -> Unit)

@ServiceContract
interface Factory {
fun create(init: TextComponentScope.() -> Unit): TextComponent
}
}

fun textComponent(init: TextComponentScope.() -> Unit): TextComponent {
val loader = ServiceLoader.load(TextComponentScope.Factory::class.java)
val apiClassLoader = TextComponentScope::class.java.classLoader

val factory =
loader.firstOrNull {
it.javaClass.classLoader != apiClassLoader
} ?: loader.firstOrNull()
?: error("No TextComponentScope.Factory implementation found")

return factory.create(init)
}
10 changes: 10 additions & 0 deletions modules/core/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
plugins {
id 'org.jetbrains.kotlin.kapt'
}

dependencies {
implementation(project(":core-api"))

compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:${versions.spi}"
kapt "io.github.eventhorizonlab:spi-tooling-processor:${versions.spi}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.github.eventhorizonlab.kyoriadventuredsl.core

import com.github.eventhorizonlab.core.api.TextComponentScope
import com.github.eventhorizonlab.spi.ServiceProvider
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.TextComponent
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextDecoration

internal class DefaultTextComponentScope(
private val builder: TextComponent.Builder
) : TextComponentScope {
override fun content(content: String) {
builder.content(content)
}

override fun color(color: NamedTextColor) {
builder.color(color)
}

override fun decorate(vararg decorations: TextDecoration) {
decorations.forEach { builder.decoration(it, true) }
}

override fun text(init: TextComponentScope.() -> Unit) {
val childBuilder = Component.text()
DefaultTextComponentScope(childBuilder).init()
builder.append(childBuilder.build())
}

fun build() = builder.build()

@ServiceProvider(TextComponentScope.Factory::class)
class Factory : TextComponentScope.Factory {
override fun create(init: TextComponentScope.() -> Unit): TextComponent {
val builder = Component.text()
return DefaultTextComponentScope(builder).apply(init).build()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.github.eventhorizonlab.kyoriadventuredsl.core

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.instanceOf
import net.kyori.adventure.text.TextComponent
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextDecoration

class TextComponentScopeTest :
StringSpec({

// Use the concrete Factory to create components
val factory = DefaultTextComponentScope.Factory()

"builds a simple text component with content" {
val content = "Hello World!"
val component =
factory.create {
content(content)
}

component.content() shouldBe content
component.color() shouldBe null
component.decorations().forEach { (_, state) ->
state shouldBe TextDecoration.State.NOT_SET
}
}

"applies a color to the text" {
val color = NamedTextColor.RED
val component =
factory.create {
color(color)
}

component.color() shouldBe color
}

"applies multiple decorations" {
val decorations = arrayOf(TextDecoration.BOLD, TextDecoration.ITALIC)
val component =
factory.create {
decorate(*decorations)
}

decorations.forEach {
component.hasDecoration(it) shouldBe true
component.decoration(it) shouldBe TextDecoration.State.TRUE
}
}

"supports nested text components" {
val component =
factory.create {
content("Parent")
text {
content(" Child")
color(NamedTextColor.GREEN)
}
}

component.children().size shouldBe 1
val child = component.children().first()
child shouldBe instanceOf<TextComponent>()
val textComponent = child as TextComponent
textComponent.content() shouldBe " Child"
textComponent.color() shouldBe NamedTextColor.GREEN
}
})
8 changes: 6 additions & 2 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@ plugins {

rootProject.name = 'KyoriAdventureDSL'

include 'core'
project(':core').projectDir = file('modules/core')
['core', 'core-api'].forEach {
include it
project(":$it").projectDir = file("modules/$it")
}

include 'e2e'