Skip to content

Conversation

@skydoves
Copy link
Owner

@skydoves skydoves commented Nov 24, 2025

Support KMP js.

Summary by CodeRabbit

  • New Features
    • Extended multiplatform build configuration to support JavaScript compilation for both browser and Node.js runtime environments
    • Added browser console logging capabilities for recomposition events in JavaScript, providing developers with detailed diagnostics to monitor and debug composable recomposition behavior

✏️ Tip: You can customize this high-level summary in your review settings.

@skydoves skydoves self-assigned this Nov 24, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Walkthrough

The pull request adds JavaScript target support to the Kotlin Multiplatform build configuration and provides a platform-specific implementation of DefaultRecompositionLogger for the JS target. The changes enable compilation for both browser and Node.js environments and include recomposition event logging to the console.

Changes

Cohort / File(s) Change Summary
Kotlin Multiplatform JS Target Configuration
stability-runtime/build.gradle.kts
Adds js { browser(); nodejs() } block to the top-level kotlin { } configuration and enables withJs() in the hierarchical template to include JS in common targets alongside existing JVM/Android/Native platforms.
JS Recomposition Logger Implementation
stability-runtime/src/jsMain/kotlin/com/skydoves/compose/stability/runtime/DefaultRecompositionLogger.js.kt
Implements a Kotlin/JS actual class that extends RecompositionLogger and logs recomposition events to the browser console. Provides formatted output with parameter change tracking (changed/stable/unstable states) and defensive string conversion via a safeToString helper to prevent runtime errors.

Sequence Diagram

sequenceDiagram
    actor App as App/Runtime
    participant Logger as DefaultRecompositionLogger.js
    participant Console as Browser Console

    App->>Logger: log(recompositionEvent)
    
    rect rgb(200, 220, 255)
    Note over Logger: Format header with count & composable name
    Logger->>Console: console.log(header)
    end
    
    rect rgb(200, 220, 255)
    loop For each parameter change
        alt changed
            Logger->>Logger: safeToString(oldValue)
            Logger->>Logger: safeToString(newValue)
        else stable/unstable
            Logger->>Logger: safeToString(newValue)
        end
        Logger->>Console: console.log(formatted change line)
    end
    end
    
    opt If unstable parameters exist
    rect rgb(220, 200, 220)
    Logger->>Console: console.log(unstable summary)
    end
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • Build configuration changes are straightforward additions following established patterns.
  • JS implementation is relatively simple with defensive error handling in safeToString.
  • Verify that console logging output format aligns with expected debugging experience.

Possibly related PRs

  • Support wasmJs in multiplatform #36: Adds WebAssembly (wasmJs) multiplatform target with a corresponding actual implementation of DefaultRecompositionLogger, following the same pattern as this JS support addition.

Poem

🐰 JavaScript joins the native array,
Console logs dance in a browser's display,
From Kotlin's multiplatform spree,
Recomposition events flow wild and free—
Stability traces, now logged in JS! 📜✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is severely incomplete, missing all required sections from the template including Goal, Implementation details, and Explain examples. Provide a comprehensive description following the template: explain the goal/motivation, detail the implementation approach, include code examples, and confirm spotlessApply and apiDump have been run.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Support KMP js' is vague and generic, using non-descriptive terms that don't clearly convey what specific KMP/JS functionality is being added. Make the title more specific and descriptive, such as 'Add JavaScript target support to Kotlin Multiplatform build configuration' to clarify the specific changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kmp/js

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 355e4e1 and dc11d58.

⛔ Files ignored due to path filters (1)
  • kotlin-js-store/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (2)
  • stability-runtime/build.gradle.kts (2 hunks)
  • stability-runtime/src/jsMain/kotlin/com/skydoves/compose/stability/runtime/DefaultRecompositionLogger.js.kt (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Spotless check
  • GitHub Check: Build and Tests
  • GitHub Check: API check
  • GitHub Check: Gradle Plugin Tests
  • GitHub Check: Lint Module Tests
  • GitHub Check: Compiler Plugin Tests
  • GitHub Check: Runtime Module Tests
  • GitHub Check: Compiler Tests (FIR/IR Dumps)
🔇 Additional comments (5)
stability-runtime/build.gradle.kts (2)

35-38: LGTM! JS target configuration is correct.

The JS target is properly configured for both browser and Node.js environments, following standard Kotlin Multiplatform conventions.


49-49: LGTM! JS properly included in hierarchy.

The withJs() call correctly includes the JS target in the common hierarchy template, ensuring the JS platform has access to shared code.

stability-runtime/src/jsMain/kotlin/com/skydoves/compose/stability/runtime/DefaultRecompositionLogger.js.kt (3)

30-31: LGTM! Proper expect/actual implementation.

The class declaration correctly implements the actual pattern for the JS target.


34-36: LGTM! Header logging is clean.

The header format and use of console.log is appropriate for the JavaScript environment.


67-76: LGTM! Robust error handling.

The safeToString helper provides good defensive coding with appropriate fallback for any toString() failures.

Comment on lines +39 to +60
event.parameterChanges.forEachIndexed { index, change ->
val isLast = index == event.parameterChanges.size - 1
val prefix = if (isLast) " └─" else " ├─"

val status = when {
change.changed -> {
val oldStr = safeToString(change.oldValue)
val newStr = safeToString(change.newValue)
"changed ($oldStr → $newStr)"
}

change.stable -> "stable (${safeToString(change.newValue)})"
else -> "unstable (${safeToString(change.newValue)})"
}

console.log("$prefix ${change.name}: ${change.type} $status")
}

// Log unstable parameters summary
if (event.unstableParameters.isNotEmpty()) {
console.log(" └─ Unstable parameters: ${event.unstableParameters}")
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix tree structure when unstable parameters are present.

The tree prefix logic doesn't account for the unstable parameters line. When unstableParameters is not empty, the last parameter change incorrectly uses "└─" (line 41), but then the unstable parameters line (line 59) also uses "└─", creating two terminal branches. This contradicts the example in your documentation (lines 23-27), which correctly shows the last parameter using "├─" when followed by the unstable line.

Apply this diff to fix the tree structure:

   // Log parameter changes
   event.parameterChanges.forEachIndexed { index, change ->
-    val isLast = index == event.parameterChanges.size - 1
+    val isLast = index == event.parameterChanges.size - 1 && event.unstableParameters.isEmpty()
     val prefix = if (isLast) "  └─" else "  ├─"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
event.parameterChanges.forEachIndexed { index, change ->
val isLast = index == event.parameterChanges.size - 1
val prefix = if (isLast) " └─" else " ├─"
val status = when {
change.changed -> {
val oldStr = safeToString(change.oldValue)
val newStr = safeToString(change.newValue)
"changed ($oldStr$newStr)"
}
change.stable -> "stable (${safeToString(change.newValue)})"
else -> "unstable (${safeToString(change.newValue)})"
}
console.log("$prefix ${change.name}: ${change.type} $status")
}
// Log unstable parameters summary
if (event.unstableParameters.isNotEmpty()) {
console.log(" └─ Unstable parameters: ${event.unstableParameters}")
}
event.parameterChanges.forEachIndexed { index, change ->
val isLast = index == event.parameterChanges.size - 1 && event.unstableParameters.isEmpty()
val prefix = if (isLast) " └─" else " ├─"
val status = when {
change.changed -> {
val oldStr = safeToString(change.oldValue)
val newStr = safeToString(change.newValue)
"changed ($oldStr$newStr)"
}
change.stable -> "stable (${safeToString(change.newValue)})"
else -> "unstable (${safeToString(change.newValue)})"
}
console.log("$prefix ${change.name}: ${change.type} $status")
}
// Log unstable parameters summary
if (event.unstableParameters.isNotEmpty()) {
console.log(" └─ Unstable parameters: ${event.unstableParameters}")
}
🤖 Prompt for AI Agents
In
stability-runtime/src/jsMain/kotlin/com/skydoves/compose/stability/runtime/DefaultRecompositionLogger.js.kt
around lines 39 to 60, the tree prefix for the last parameter change doesn't
consider the unstableParameters line, causing two "└─" terminal branches; update
the logic that sets isLast so it only returns true when this parameter is the
final line overall (i.e., index == event.parameterChanges.size - 1 AND
event.unstableParameters is empty), otherwise treat it as non-terminal so the
last parameter uses "├─" when an unstableParameters summary will follow.

@skydoves skydoves merged commit 80a325f into main Nov 24, 2025
10 checks passed
@skydoves skydoves deleted the kmp/js branch November 24, 2025 05:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants