Development
May 22, 2026 · View on GitHub
Instructions for building the project from source and running it locally against the bundled samples, without going through Maven Central or the VS Code Marketplace.
There are three shipping artifacts, each with its own local install story:
| Artifact | Built by | Consumed via |
|---|---|---|
Gradle plugin (ee.schimke.composeai.preview) | includeBuild (in-repo, preferred) or ./gradlew :gradle-plugin:publishToMavenLocal | includeBuild("…/gradle-plugin") or mavenLocal() in a consumer's settings.gradle.kts |
CLI (compose-preview) | ./gradlew :cli:installDist | Symlink into ~/.local/bin (or add to $PATH) |
| VS Code extension | cd vscode-extension && npm install && npm run compile | Extension Dev Host (F5), folder symlink, or .vsix |
Prerequisites
- Java 17 (
JAVA_HOMEset) - Gradle wrapper ships with the repo — do not install Gradle separately
- Node 20+ and
npm(for the VS Code extension) - VS Code 1.85+ (only if working on the extension)
The bundled samples (samples/android/,
samples/cmp/) depend on the gradle-plugin module via
includeBuild, so running ./gradlew at the repo
root always picks up local plugin changes without any publishing step.
Gradle plugin
Inside this repo, the samples pick up the plugin automatically through the composite build. Nothing to do.
For an external consumer project on the same machine, pick one of the two approaches below.
Option A — Composite build (recommended)
Have the consumer include this repo's gradle-plugin/ directory directly. No
publish step, no cached artifacts to go stale.
// consumer's settings.gradle.kts
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
includeBuild("../compose-ai-tools/gradle-plugin")
}
// consumer's <module>/build.gradle.kts
plugins {
id("ee.schimke.composeai.preview") // no version — resolved from includeBuild
}
Edits to plugin source rebuild automatically on the next consumer build.
Option B — publishToMavenLocal
Useful when you want to pin a specific built version, or when the consumer can't reach the plugin sources (e.g. Docker build, other machine via rsync).
./gradlew :gradle-plugin:publishToMavenLocal
Then in the consumer's settings.gradle.kts, add mavenLocal() to the
plugin repositories:
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
mavenLocal()
}
}
And apply the plugin with whatever version the local build produced:
plugins {
id("ee.schimke.composeai.preview") version "0.3.4-SNAPSHOT"
}
The version string must match the one in
gradle-plugin/build.gradle.kts — the
local fallback (used when PLUGIN_VERSION is not set) tracks the next
upcoming release (e.g. after tagging v0.3.3, the fallback is
0.3.4-SNAPSHOT).
Smoke test
./gradlew :samples:cmp:composePreviewRenderAll
open samples/cmp/build/compose-previews/renders/
CLI
Build the install image (a native-ish shell launcher + all jars on the classpath):
./gradlew :cli:installDist
Output: cli/build/install/compose-preview/bin/compose-preview.
Wire it onto your $PATH — easiest is a symlink into a directory already on
$PATH:
ln -sf "$PWD/cli/build/install/compose-preview/bin/compose-preview" ~/.local/bin/compose-preview
Verify against a sample:
cd samples/cmp
compose-preview list
compose-preview show --filter RedBox
Re-running ./gradlew :cli:installDist after code changes refreshes the
launcher — the symlink does not need to be re-created.
VS Code extension
Install dev dependencies and compile TypeScript once:
cd vscode-extension
npm install
npm run compile
Three ways to run the extension locally, in order of preference for day-to-day work:
Option 1 — Extension Development Host (recommended while coding)
- Open vscode-extension/ as its own VS Code window.
- Press
F5(or Run → Start Debugging). - A second VS Code window opens with the extension loaded against the
repo root — both samples/android/ and
samples/cmp/ are visible, and the module picker at the
top of the Compose Preview panel lets you switch between them. Edit
TypeScript, press
Ctrl+Shift+F5to reload.
The launch config lives at vscode-extension/.vscode/launch.json with four variants: repo root (default), samples/cmp only, samples/android only, and an empty host. Pick from the Run & Debug dropdown before pressing F5.
For a tighter edit/reload cycle, run npm run watch in a terminal — the
compile-on-save will be picked up by the next host reload.
Option 2 — Symlink into the user extensions directory
Useful when you want the extension active in your everyday VS Code without
running a second window. Make sure out/ is built (npm run compile), then:
ln -sfn "$PWD" ~/.vscode/extensions/schimke.compose-preview-dev
Fully restart VS Code (reloading the window is not always enough on first
install). To update, just re-run npm run compile — VS Code picks up the new
out/*.js next time it activates the extension.
To uninstall: rm ~/.vscode/extensions/schimke.compose-preview-dev.
Do not also have the Marketplace version installed — VS Code will load both and you'll see duplicate views.
Option 3 — Build and install a .vsix
Closest to what a real user gets:
npm run package
code --install-extension compose-preview-0.3.3.vsix
The package script runs vsce package --no-dependencies, which requires
out/ to already be built (the compile script is chained in automatically).
Running the test suites
./gradlew check # gradle-plugin unit + functional tests, CLI tests
cd vscode-extension && npm test # extension unit tests (mocha)
Troubleshooting
IllegalAccessException: … DirectByteBuffer … modifiers "public"
Robolectric's ShadowVMRuntime.getAddressOfDirectByteBuffer reflects into
java.nio.DirectByteBuffer.address(); under JDK 17+ module rules that fails
without --add-opens=java.base/java.nio=ALL-UNNAMED. The composePreviewRender
Gradle task already adds that opens (along with java.lang and
java.lang.reflect) in
ComposePreviewPlugin.kt;
if a downstream Test task hits this error, mirror the JVM args.
jlink executable … does not exist when building from VS Code
The vscjava.vscode-gradle extension inherits its JDK from
redhat.java, which ships a JRE (no jlink). The Android Gradle
Plugin's JdkImageTransform needs jlink, so Android builds triggered
from inside VS Code fail with:
jlink executable …/redhat.java-*/jre/*/bin/jlink does not exist.
Point both the Java language server and the Gradle integration at a
full JDK 17 install in your user settings.json:
{
"java.jdt.ls.java.home": "/usr/lib/jvm/java-17-openjdk",
"java.import.gradle.java.home": "/usr/lib/jvm/java-17-openjdk"
}
Adjust the path for your OS (/Library/Java/JavaVirtualMachines/…/Contents/Home
on macOS, C:\\Program Files\\Java\\jdk-17 on Windows). Reload the
window after changing. Verify with ls $JDK_PATH/bin/jlink — it must
exist.
Testing a downstream project against a -SNAPSHOT
Every push to main publishes <next-patch>-SNAPSHOT to Central
snapshots. For PR testing, run the Publish snapshot workflow
manually from the branch — it produces a branch-qualified version
(<next-patch>-<branch-name>-<short-sha>-SNAPSHOT) that won't collide
with main. See RELEASING.md § Snapshots for
the full recipe and consumer-side pluginManagement block.