Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 11 additions & 1 deletion .github/workflows/compose-html-eap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- 'html/settings.gradle.kts'
- 'html/gradle.properties'
- 'html/buildSrc/**'
- 'html/gradle/**'
- '.github/workflows/compose-html-eap.yml'
push:
branches:
Expand All @@ -22,6 +23,7 @@ on:
- 'html/settings.gradle.kts'
- 'html/gradle.properties'
- 'html/buildSrc/**'
- 'html/gradle/**'
- '.github/workflows/compose-html-eap.yml'

jobs:
Expand Down Expand Up @@ -50,6 +52,14 @@ jobs:
:html-core-eap:check \
:html-svg-eap:check \
:internal-html-core-runtime-eap:check \
:internal-html-serialization-eap:check \
:html-core-eap:publishToMavenLocal \
:html-svg-eap:publishToMavenLocal \
:internal-html-core-runtime-eap:publishToMavenLocal
:internal-html-core-runtime-eap:publishToMavenLocal \
:internal-html-serialization-eap:publishToMavenLocal

- name: Test HTML compiler and Gradle plugins, including browser hydration
working-directory: html
run: |
./gradlew --no-daemon --stacktrace --max-workers=1 \
-Pcompose.html.eap.enabled=true :compose-html-compiler-plugin:check
22 changes: 19 additions & 3 deletions html/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ val localProperties = java.util.Properties().apply {
file.inputStream().use(::load)
}
}

fun setting(name: String): String? =
localProperties.getProperty(name) ?: providers.gradleProperty(name).orNull

Expand All @@ -32,11 +33,12 @@ kotlinKarmaConfig.rootDir = rootProject.rootDir.toString()

apply<SeleniumDriverPlugin>()

fun Project.isSampleProject() = projectDir.parentFile.name == "examples"
// Only the browser samples have bundle-size tasks; EAP examples also include JVM-only projects.
fun Project.isSampleProject() = path.startsWith(":examples:")

tasks.register("printBundleSize") {
dependsOn(
subprojects.filter { it.isSampleProject() }.map { ":examples:${it.name}:printBundleSize" }
subprojects.filter { it.isSampleProject() }.map { "${it.path}:printBundleSize" }
)
}

Expand All @@ -48,6 +50,7 @@ subprojects {
"html-core-eap",
"html-svg-eap",
"internal-html-core-runtime-eap",
"internal-html-serialization-eap",
)
if (isComposeHtmlEapProject) {
group = "org.jetbrains.compose.html.eap"
Expand Down Expand Up @@ -90,7 +93,10 @@ subprojects {
repositories {
maven {
name = "internal"
url = uri(COMPOSE_REPO_URL ?: System.getenv("COMPOSE_REPO_URL") ?: "https://packages.jetbrains.team/maven/p/cmp/dev")
url = uri(
COMPOSE_REPO_URL ?: System.getenv("COMPOSE_REPO_URL")
?: "https://packages.jetbrains.team/maven/p/cmp/dev"
)
credentials {
username = COMPOSE_REPO_USERNAME ?: System.getenv("COMPOSE_REPO_USERNAME") ?: ""
password = COMPOSE_REPO_KEY ?: System.getenv("COMPOSE_REPO_KEY") ?: ""
Expand All @@ -106,17 +112,27 @@ subprojects {
"html-svg-eap" -> "JetBrains Compose HTML EAP SVG"
"internal-html-core-runtime-eap" ->
"JetBrains Compose HTML EAP internal runtime"

"internal-html-serialization-eap" ->
"JetBrains Compose HTML EAP internal serialization"

else -> "JetBrains Compose HTML library"
}
)
description.set(
when (projectName) {
"html-core-eap" ->
"Experimental commonized JetBrains Compose HTML core library"

"html-svg-eap" ->
"Experimental commonized JetBrains Compose HTML SVG library"

"internal-html-core-runtime-eap" ->
"Experimental commonized JetBrains Compose HTML internal runtime"

"internal-html-serialization-eap" ->
"Shared internal serialization rules for JetBrains Compose HTML EAP and its compiler plugin"

else -> "JetBrains Compose HTML library"
}
)
Expand Down
16 changes: 12 additions & 4 deletions html/eap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ Fast validation is the default. Set `COMPOSE_HTML_VALIDATE_STRICTLY=true` on the
with strict validation. Browser hydration follows the server's mode. Direct callers can pass
`validateStrictly = true` to `hydrateComposable`.

| During hydration | Fast (default) | Strict |
| --- | --- | --- |
| During hydration | Fast (default) | Strict |
|------------------------------------|--------------------------------------------|----------------------------------------------|
| Text, attributes, or styles differ | Keeps server values until a Compose update | Reports a mismatch and renders on the client |
| `allowHydrationMismatch()` | Applies client values | Applies client values |
| Structure differs | Renders on the client | Renders on the client |
| `allowHydrationMismatch()` | Applies client values | Applies client values |
| Structure differs | Renders on the client | Renders on the client |

Strict mode also checks tag and attribute names, class tokens,
foreign-attribute collisions and table-element nesting.
Expand All @@ -47,11 +47,19 @@ dependencies {
implementation("org.jetbrains.compose.html:kotlinx-browser-common-subset:<version>")
}
```

Local build versions are configured in `html/gradle.properties`.

Do not put the regular and EAP Compose HTML artifacts on the same compilation classpath because
they intentionally expose the same Kotlin packages and declarations.

## Static HTML lowering

The HTML compiler plugin prepares static HTML at compile time to reduce work during
server-side rendering on the JVM. Dynamic content is still rendered at runtime.

See the [plugin README](compiler-plugin/README.md#activate) for activation and configuration.

## Examples

### String Rendering
Expand Down
46 changes: 46 additions & 0 deletions html/eap/compiler-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Compose HTML compiler plugin

Prepares static HTML at compile time to reduce work during server-side rendering.

## Activate

Use the [Compose HTML EAP runtime](../README.md#published-artifacts).
Add the plugin to your module's `build.gradle.kts` alongside your existing Kotlin and Compose compiler plugins:

```kotlin
plugins {
id("org.jetbrains.compose.html.lowering") version "<version>"
}
```

Lowering is enabled for JVM targets.
The Gradle plugin adds the compiler plugin and sets the required compiler order.

### Using a local checkout

To resolve both plugins from source, add these entries to your project's `settings.gradle.kts`:

```kotlin
pluginManagement {
includeBuild("/path/to/html/eap/compiler-plugin/gradle-plugin")
// Keep your existing plugin repositories here.
}

includeBuild("/path/to/html")
```

For the local EAP runtime setup, see the [EAP README](../README.md).

## Configure

Optional settings in your module's `build.gradle.kts`:

```kotlin
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType

htmlLowering {
enabledTargets.set(setOf(KotlinPlatformType.jvm, KotlinPlatformType.androidJvm))
}
```

Currently, only JVM rendering is supported. Set it to `emptySet()` to disable lowering for all targets.
57 changes: 57 additions & 0 deletions html/eap/compiler-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2026 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/

import java.util.Properties

plugins {
kotlin("jvm")
}

val pluginProperties = Properties().apply {
file("plugin.properties").inputStream().use(::load)
}
val kotlinVersion = Properties().apply {
rootProject.file("buildSrc/gradle.properties").inputStream().use(::load)
}.getProperty("kotlin.version")

group = "org.jetbrains.compose.html"
version = pluginProperties.getProperty("version")

val compilerPluginPath = path
subprojects {
// Resolve the compiler artifact from this build while developing the Gradle plugin.
configurations.configureEach {
resolutionStrategy.dependencySubstitution {
substitute(module("org.jetbrains.compose.html:compose-html-compiler-plugin"))
.using(project(compilerPluginPath))
}
}
}

// The HTML root build targets JVM 11. Gradle requires Java to use the same target.
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

dependencies {
implementation(project(":internal-html-serialization-eap"))
compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlinVersion")
}

publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
}

tasks.named("check") {
dependsOn(
"$compilerPluginPath:tests:check",
gradle.includedBuild("compose-html-lowering-gradle-plugin").task(":test")
)
}
63 changes: 63 additions & 0 deletions html/eap/compiler-plugin/gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2026 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/

import java.util.Properties
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
kotlin("jvm")
`java-gradle-plugin`
}

val pluginProperties = Properties().apply {
file("../plugin.properties").inputStream().use(::load)
}
val kotlinVersion = Properties().apply {
file("../../../buildSrc/gradle.properties").inputStream().use(::load)
}.getProperty("kotlin.version")

group = "org.jetbrains.compose.html"
version = pluginProperties.getProperty("version")

repositories {
mavenCentral()
}

// The HTML root build targets JVM 11. Gradle requires Java to use the same target.
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}

dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin-api:$kotlinVersion")
testImplementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
testImplementation(kotlin("test-junit"))
}

gradlePlugin {
plugins {
create("htmlLowering") {
id = pluginProperties.getProperty("id")
implementationClass = "org.jetbrains.compose.html.gradle.HtmlLoweringGradlePlugin"
}
}
}

tasks.test {
// Gradle ProjectBuilder injects synthetic classes through java.lang on JDK 21.
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
}

// The plugin needs its coordinates after it has been packaged and installed elsewhere.
tasks.processResources {
from("../plugin.properties") { rename { "compose-html-lowering.properties" } }
}
19 changes: 19 additions & 0 deletions html/eap/compiler-plugin/gradle-plugin/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2026 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/

pluginManagement {
val kotlinVersion = java.util.Properties().apply {
file("../../../buildSrc/gradle.properties").inputStream().use(::load)
}.getProperty("kotlin.version")
plugins {
kotlin("jvm") version kotlinVersion
}
repositories {
gradlePluginPortal()
mavenCentral()
}
}

rootProject.name = "compose-html-lowering-gradle-plugin"
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2026 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/

package org.jetbrains.compose.html.gradle

import org.gradle.api.provider.SetProperty
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType

/**
* Configures HTML lowering for Kotlin compilations. Enabled by default for JVM targets.
*
* ```kotlin
* htmlLowering {
* enabledTargets.set(setOf(KotlinPlatformType.jvm, KotlinPlatformType.androidJvm))
* }
* ```
*/
abstract class HtmlLoweringExtension {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep just one property?

val enabledTargets: SetProperty< KotlinPlatformType>

With Jvm enabled by default

/**
* Kotlin platform types for which lowering is enabled. Defaults to [KotlinPlatformType.jvm].
* Set to an empty set to disable lowering for all targets.
* Enabling an unsupported compiler platform reports a warning and skips lowering.
*/
abstract val enabledTargets: SetProperty<KotlinPlatformType>
}
Loading
Loading