Skip to content
Open
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
6 changes: 6 additions & 0 deletions build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
compileOnly(libs.spotless.gradle)
implementation(libs.truth)
compileOnly(libs.androidx.room.gradle.plugin)
compileOnly(libs.sqldelight.gradlePlugin)
compileOnly(libs.firebase.crashlytics.gradlePlugin)
compileOnly(libs.firebase.performance.gradlePlugin)
}
Expand Down Expand Up @@ -125,5 +126,10 @@ gradlePlugin {
description = "Configures Room for the project"
}

register("KMPSQLDelight") {
id = "mifos.kmp.sqldelight"
implementationClass = "KMPSQLDelightConventionPlugin"
description = "Configures SQLDelight for the project"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import com.android.build.gradle.LibraryExtension
import org.convention.configureFlavors
import org.convention.configureKotlinAndroid
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import app.cash.sqldelight.gradle.SqlDelightExtension
import org.convention.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.internal.Actions.with
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies


private const val DATABASE_NAME = "MifosSQLDelightDatabase"

class KMPSQLDelightConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("app.cash.sqldelight")

extensions.configure<SqlDelightExtension> {
databases.create(DATABASE_NAME) {
packageName.set("org.mifos.core.database")
generateAsync.set(true)
schemaOutputDirectory.set(
file("$projectDir/schemas")
)
verifyMigrations.set(true)
}
}


dependencies {
add("androidMainImplementation",libs.findLibrary("sqldelight.android.driver").get())
add("nativeMainImplementation",libs.findLibrary("sqldelight.native.driver").get())
add("desktopMainImplementation",libs.findLibrary("sqldelight.sqlite.driver").get())
add("jsMainImplementation",libs.findLibrary("sqldelight.web.worker.driver").get())
add("wasmJsMainImplementation",libs.findLibrary("sqldelight.web.worker.driver").get())

add("commonMainImplementation", libs.findLibrary("sqldelight.coroutines").get())
add("commonMainImplementation", libs.findLibrary("sqldelight.primitive.adapters").get())
}

}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ internal fun Project.configureKotlinMultiplatform() {
iosX64()
iosArm64()
js(IR) {
this.nodejs()
browser()
nodejs()
binaries.executable()
}
wasmJs() {
Expand Down
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ plugins {
alias(libs.plugins.ktrofit) apply false

alias(libs.plugins.room) apply false
alias(libs.plugins.sqldelight) apply false
}

object DynamicVersion {
Expand Down
1 change: 1 addition & 0 deletions cmp-navigation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ kotlin {
implementation(projects.core.model)
implementation(projects.core.common)
implementation(projects.core.datastore)
implementation(projects.core.database)

implementation(projects.core.datastore)
implementation(projects.coreBase.common)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import cmp.navigation.rootnav.RootNavViewModel
import org.koin.core.module.dsl.viewModelOf
import org.koin.dsl.module
import org.mifos.core.data.di.DataModule
import org.mifos.core.database.di.DatabaseModule
import org.mifos.core.datastore.di.DatastoreModule
import org.mifos.feature.home.di.HomeModule
import org.mifos.feature.settings.SettingsModule
Expand Down Expand Up @@ -51,6 +52,7 @@ object KoinModules {
dispatcherModule,
analyticsModule,
DatastoreModule,
DatabaseModule,
featureModule,
AppModule,
)
Expand Down
2 changes: 1 addition & 1 deletion cmp-shared/cmp_shared.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |spec|
spec.name = 'cmp_shared'
spec.version = '2026.2.4'
spec.version = '2026.3.2'
spec.homepage = 'https://github.com/openMF/kmp-project-template'
spec.source = { :http=> ''}
spec.authors = ''
Expand Down
32 changes: 32 additions & 0 deletions cmp-web/karma.config.d/sqljs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const path = require("path");
const os = require("os");
const wasm = require.resolve("sql.js/dist/sql-wasm.wasm").replace(/\\/g, "/");

config.files.push({
pattern: wasm,
served: true,
watched: false,
included: false,
nocache: false,
});

if (!config.proxies) {
config.proxies = {};
}
config.proxies["/sql-wasm.wasm"] = `/absolute${wasm}`

// Adapted from: https://github.com/ryanclark/karma-webpack/issues/498#issuecomment-790040818
const output = {
path: path.join(os.tmpdir(), '_karma_webpack_') + Math.floor(Math.random() * 1000000),
}
config.set({
webpack: {...config.webpack, output}
});
config.files.push({
pattern: `${output.path}/**/*`,
watched: false,
included: false,
});

// TODO: Figure out why on earth this is necessary. Presumably a karma-webpack bug???
delete config.webpack.optimization;
16 changes: 16 additions & 0 deletions cmp-web/webpack.config.d/sqljs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
config.resolve = {
fallback: {
fs: false,
path: false,
crypto: false,
}
};

const CopyWebpackPlugin = require('copy-webpack-plugin');
config.plugins.push(
new CopyWebpackPlugin({
patterns: [
'../../node_modules/sql.js/dist/sql-wasm.wasm'
]
})
);
12 changes: 1 addition & 11 deletions core-base/database/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,7 @@
*
* See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
import org.jetbrains.compose.compose

/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
plugins {
alias(libs.plugins.kmp.core.base.library.convention)
}
Expand All @@ -28,6 +18,7 @@ android {

kotlin {
sourceSets {

androidMain.dependencies {
implementation(libs.androidx.room.runtime)
}
Expand All @@ -39,7 +30,6 @@ kotlin {
nativeMain.dependencies {
implementation(libs.androidx.room.runtime)
}

nonJsCommonMain.dependencies {
implementation(libs.androidx.room.runtime)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
*/
package org.mifos.core.data.di

import android.content.Context
import org.koin.android.ext.koin.androidContext
import org.koin.core.module.Module
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind
Expand All @@ -22,7 +20,5 @@ import template.core.base.common.di.CommonModule
actual val platformModule: Module = module {
includes(CommonModule)

single<Context> { androidContext() }

singleOf(::TimeZoneMonitorImpl) bind TimeZoneMonitor::class
}
131 changes: 129 additions & 2 deletions core/database/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
/*
* Copyright 2026 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
import org.gradle.kotlin.dsl.invoke

/*
* Copyright 2025 Mifos Initiative
*
Expand All @@ -12,35 +23,151 @@ plugins {
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.mifos.kmp.room)
alias(libs.plugins.mifos.kmp.sqldelight)
}

android {
namespace = "org.mifos.core.database"
}

val isWindows = System.getProperty("os.name").lowercase().contains("win")

fun isInstalled(binary: String) = try {
val command = if (isWindows) arrayOf("where", binary) else arrayOf("which", binary)
Runtime.getRuntime().exec(command).waitFor() == 0
} catch (e: Exception) {
false
}

kotlin {
js(IR) {
browser {
testTask {
useKarma {
// Update this block to use your preferred browser for running tests.
when {
isInstalled("google-chrome") || isInstalled("chrome") -> useChrome()
isInstalled("chromium") || isInstalled("chromium-browser") -> useChrome()
isInstalled("brave") || isInstalled("brave-browser") -> useChrome()
isInstalled("microsoft-edge") || isInstalled("microsoft-edge-stable") || isInstalled("msedge") -> useChrome()
isInstalled("firefox") -> useFirefox()
isInstalled("opera") -> useOpera()
isInstalled("safari") -> useSafari()
else -> useChrome()
}
}
if (!isInstalled("google-chrome") && !isInstalled("chrome")) {
when {
isInstalled("chromium") -> environment("CHROME_BIN", "chromium")
isInstalled("chromium-browser") -> environment("CHROME_BIN", "chromium-browser")
isInstalled("brave") -> environment("CHROME_BIN", "brave")
isInstalled("brave-browser") -> environment("CHROME_BIN", "brave-browser")
isInstalled("microsoft-edge") -> environment("CHROME_BIN", "microsoft-edge")
isInstalled("microsoft-edge-stable") -> environment("CHROME_BIN", "microsoft-edge-stable")
isInstalled("msedge") -> environment("CHROME_BIN", "msedge")
}
}
}
}
}
wasmJs {
browser {
testTask {
useKarma {
// Update this block to use your preferred browser for running tests.
when {
isInstalled("google-chrome") || isInstalled("chrome") -> useChrome()
isInstalled("chromium") || isInstalled("chromium-browser") -> useChrome()
isInstalled("microsoft-edge") || isInstalled("microsoft-edge-stable") || isInstalled("msedge") -> useChrome()
isInstalled("firefox") -> useFirefox()
isInstalled("brave") || isInstalled("brave-browser") -> useChrome()
isInstalled("opera") -> useOpera()
isInstalled("safari") -> useSafari()
else -> useChrome()
}
}
if (!isInstalled("google-chrome") && !isInstalled("chrome")) {
when {
isInstalled("chromium") -> environment("CHROME_BIN", "chromium")
isInstalled("chromium-browser") -> environment("CHROME_BIN", "chromium-browser")
isInstalled("brave") -> environment("CHROME_BIN", "brave")
isInstalled("brave-browser") -> environment("CHROME_BIN", "brave-browser")
isInstalled("microsoft-edge") -> environment("CHROME_BIN", "microsoft-edge")
isInstalled("microsoft-edge-stable") -> environment("CHROME_BIN", "microsoft-edge-stable")
isInstalled("msedge") -> environment("CHROME_BIN", "msedge")
}
}
}
}
}
sourceSets {
val desktopMain by getting
androidMain.dependencies {
implementation(libs.koin.android)
implementation(libs.androidx.room.runtime)
implementation(libs.sqldelight.runtime)
implementation(libs.sqldelight.coroutines)
implementation(libs.sqldelight.primitive.adapters)
}
androidUnitTest.dependencies {
implementation(libs.androidx.core)
implementation(libs.sqldelight.sqlite.driver)
implementation(libs.robolectric)
implementation(libs.androidx.test.ext.junit)
}

nativeMain.dependencies {
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.sqlite.bundled)
implementation(libs.sqldelight.runtime)
implementation(libs.sqldelight.coroutines)
implementation(libs.sqldelight.primitive.adapters)
}

desktopMain.dependencies {
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.sqlite.bundled)
}
implementation(libs.sqldelight.runtime)
implementation(libs.sqldelight.coroutines)
implementation(libs.sqldelight.primitive.adapters)

}
jsMain.dependencies {
implementation(libs.kotlinx.coroutines.js)
implementation(libs.sqldelight.coroutines)
implementation(libs.sqldelight.primitive.adapters)
implementation(libs.sqldelight.runtime)
implementation(npm("@cashapp/sqldelight-sqljs-worker", "2.2.1"))
implementation(npm("sql.js", "1.10.3"))
implementation(devNpm("copy-webpack-plugin", "12.0.2"))
}
wasmJsMain.dependencies {
implementation(libs.sqldelight.runtime)
implementation(libs.sqldelight.coroutines)
implementation(npm("@cashapp/sqldelight-sqljs-worker", "2.2.1"))
implementation(npm("sql.js", "1.10.3"))
implementation(devNpm("copy-webpack-plugin", "12.0.2"))
}
commonMain.dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.serialization.json)
api(projects.core.common)
api(projects.coreBase.database)
}
commonTest.dependencies {
implementation(libs.turbine)
}
}
}
tasks.register("checkSourceSets") {
doLast {
kotlin.sourceSets.forEach { ss ->
println("SourceSet: ${ss.name}")
ss.dependsOn.forEach { dep ->
println(" dependsOn: ${dep.name}")
}
}
}
}
}
kotlin.sourceSets.all { println("SourceSet: $name") }
println("jsMain depends on: " + kotlin.sourceSets.getByName("jsMain").dependsOn.map { it.name })
Loading