-
-
Notifications
You must be signed in to change notification settings - Fork 68
Description
Describe the bug
When a class containing Koin annotations (like @Single and @Named) is generated by a separate, custom KSP processor, the Koin KSP processor (koin-ksp-compiler) incorrectly assigns its definitions to the defaultModule.
This behavior does not occur if the exact same class is written manually in the source code.
The issue appears to be related to KSP processing rounds. When Koin's processor runs on the output of another processor (in a subsequent round), it seems to lose the context of the original module and falls back to the defaultModule.
This bug manifests even when defaultModule generation is disabled, resulting in fragmented and incorrect generated files:
- The intended module file (e.g.,
AppModule.kt) is generated but is empty. - An empty
KoinMeta.ktis generated. - A partial
DefaultModule.ktis generated, containing the definition that should have been inAppModule.kt. - A second
KoinMetafile is generated, mapping the class to thedefaultModule.
To Reproduce
Steps to reproduce the behavior:
- Create a multi-module project.
- In a module (e.g.,
module-b), includekoin-annotationsandkoin-ksp-compiler. - Create a custom KSP processor that generates a new class file based on some other annotation (e.g.,
@Screen). - Ensure this new, generated class file contains Koin annotations (e.g.,
@Single). - Build the project.
- Observe the KSP-generated files (in
build/generated/ksp/...). The definition from the generated class will be in aDefaultModule.ktfile, not the module file formodule-b.
I have a minimal reproduction repository available here:
https://github.com/vldi01/ModernKMPArchitecture
Expected behavior
The Koin KSP processor should treat a class generated by another KSP processor the same way it treats a manually written class.
The definitions from the generated class (e.g., BScreenInjector) should be correctly added to its own module's generated file (e.g., BModule.kt), not the defaultModule. Only two files should be generated for the module (the module definition and its KoinMeta file), just as with manually-written annotated classes.
Koin project used and used version (please complete the following information):
koin-bom:4.1.1koin-annotations:2.3.0kotlin:2.2.21kotlin-ksp:2.3.0
Additional context
The core problem is the interoperability between KSP processors.
Working Example (Manual Class)
If I write this class manually in module-a, Koin works perfectly:
// In module 'a'
@Single
@Named(type = DestinationA::class)
class AScreenInjector : ScreenInjector {
override fun getNavEntry(key: Destination): NavEntry<Destination>? {
if (key !is DestinationA) return null
return NavEntry(key) {
AScreen(key, koinViewModel { parametersOf(key) })
}
}
}Result: Koin generates AModule.kt (with the AScreenInjector singleton) and KoinMeta.kt.
Failing Example (Generated Class)
My custom processor generates this class in module-b:
// This class is generated by my custom KSP processor in module 'b'
@Single
@Named(type = DestinationB::class)
public class BScreenInjector : ScreenInjector {
override fun getNavEntry(key: Destination): NavEntry<Destination>? {
if (key !is DestinationB) return null
return NavEntry(key) {
BScreen()
}
}
}Result: Koin generates four files:
BModule.kt(empty)KoinMeta.kt(empty)DefaultModule.kt(containing theBScreenInjectorsingleton definition)KoinMeta_DefaultModule.kt(mappingBScreenInjectorto the default module)
This strongly suggests the Koin processor isn't correctly identifying the source module for KSP-generated files from a previous round.