Skip to content

Latest commit

 

History

History
219 lines (179 loc) · 10.2 KB

File metadata and controls

219 lines (179 loc) · 10.2 KB

Koin Compiler Plugin - Roadmap

Project status, completed work, and future plans.

Completed

  • Implement reified type parameter syntax single<T>(), factory<T>(), etc.
  • Implement Scope.create(::T) constructor reference syntax
  • Implement worker<T>() DSL for Android WorkManager
  • Make plugin-support a full KMP library (all Koin targets, requires Koin 4.2.0-beta3+)
  • Create stub classes for ViewModel and ListenableWorker (avoid runtime dependencies)
  • Gradle plugin auto-injects koin-annotations dependency (needs KMP support)
  • Move samples to separate folder for independent Kotlin version
  • Add Kotlin 2.2.x workarounds (stdlib for JS/WASM, disable metadata compilation)
  • Deprecate org.koin.android.annotation.KoinViewModel
  • Implement @Configuration cross-module discovery via hint functions
  • Fix KMP multi-phase FIR compilation (unique synthetic file names per class)
  • Fix expect/actual class handling (skip expect classes in FIR)
  • Fix K/Native synthetic file generation (skip on Native targets)
  • Fix cross-source-set module resolution (fallback via context.referenceFunctions)
  • Fix object module support (use irGetObject instead of constructor)
  • Add configurable logging (userLogs for component detection, debugLogs for internal processing)
  • Implement koinConfiguration<T>() DSL transformation
  • Implement KoinApplication.withConfiguration<T>() extension transformation
  • Implement @PropertyValue annotation for property defaults
  • Support definition annotations on top-level functions (discovered by @ComponentScan)

Phase 1: Packaging & Publishing ✅

Prepared for public release on Maven Central.

1.1 Build Configuration

  • Review and finalize artifact coordinates
  • Ensure proper module structure for publishing
  • Add LICENSE file (Apache 2.0)
  • Add README.md with usage instructions

1.2 Maven Central Publishing

  • Configure maven-publish plugin for all modules
  • Set up GPG signing for artifacts
  • Configure Sonatype OSSRH credentials
  • Add POM metadata (description, license, developers, SCM URLs)
  • Publish artifacts: compiler-plugin, gradle-plugin
    • Note: plugin-support is included in Koin (koin-annotations), not published separately

1.3 Gradle Plugin Portal

  • Register plugin ID on Gradle Plugin Portal
  • Configure java-gradle-plugin publishing
  • Test plugin resolution via plugins { id("io.insert-koin.compiler.plugin") }

1.4 CI/CD & Release Process

  • Set up GitHub Actions for CI (build, test)
  • Automated release workflow (tag -> publish)
  • Version management strategy (semver)

1.5 Multi-Kotlin Version Strategy

  • Deferred: Will follow latest Kotlin versions and adapt as needed
  • No complex versioning scheme required for now

Phase 2: Compile-Time Safety & Monitoring (Current)

Advanced features for production reliability.

2.1 @PropertyValue Default Values ✅

Support default values for property injection:

  • @PropertyValue("key") annotation on constant
  • Generate getProperty("key", defaultValue) calls
@PropertyValue("api.timeout")
val DEFAULT_TIMEOUT = 30000

@Factory
class ApiClient(@Property("api.timeout") val timeout: Int)
// Generates: factory { ApiClient(getProperty("api.timeout", DEFAULT_TIMEOUT)) }

2.2 Skip Default Values ✅

Skip DI injection for parameters with Kotlin default values:

  • skipDefaultValues option in koinCompiler { } DSL (default: true)
  • Non-nullable parameters with defaults use Kotlin default value
  • Nullable parameters still use getOrNull() regardless
  • Annotated parameters (@Named, @Qualifier, etc.) always injected
  • User log traces when parameters are skipped
class Service(val a: A, val name: String = "default")
single<Service>()
// With skipDefaultValues=true:  Service(scope.get())  -- name uses default
// With skipDefaultValues=false: Service(scope.get(), scope.get())

2.3 Compile-Time Dependency Validation ✅

Detect missing dependencies at compile time instead of runtime crashes.

  • Per-module validation (A1): local definitions + explicit includes
  • Validate non-nullable parameters have definitions
  • Validate Lazy<T> inner type is provided
  • Validate @Named/@Qualifier qualifiers match (with hints for mismatches)
  • Validate scoped dependencies are in correct scope
  • Skip safe parameters: nullable, @InjectedParam, @Property, List<T>, defaults
  • Clear error messages with module/parameter context
  • Configuration group validation (A2): @Configuration sibling modules share definitions
  • startKoin full-graph validation (A3): validates all modules assembled by startKoin<T>()
  • Cross-Gradle-module validation (C): definitions from dependency JARs via hint functions
  • @Provided annotation: marks types as externally available, skips safety validation
  • Android framework whitelist: Context, Activity, Application, Fragment, SavedStateHandle, WorkerParameters
  • Cross-module function hint metadata (C2): encode qualifier, scope, bindings in hint parameters
    • Qualifier propagation: @Named/@Qualifier via qualifier_<name> or qualifierType hint params
    • Scope propagation: @Scope(MyScope::class) via scope hint parameter
    • Bindings propagation: return type supertypes via binding0, binding1, ... hint params
  • DSL validation (B): validate single<T>(), factory<T>() in hand-written modules
  • Call-site validation (A4): validates get<T>(), inject<T>(), koinViewModel<T>() call sites with deferred cross-module hints
  • Cross-module custom qualifier discovery via qualifier hint functions
  • See COMPILE_TIME_SAFETY.md for detailed design and implementation

Done:

  • Package filtering: scan now matches both function's package and return type's package

Remaining for 1.0:

  • Property validation (D): @Property/@PropertyValue matching — see §2.7
  • @ScopeId support — see §2.8
  • Scope parameter injection — see §2.9

2.4 @Monitor Annotation Support ✅

Function interception for logging and performance capture:

  • @Monitor annotation on class or function
  • Generate wrapper code to intercept function calls
  • Log function entry/exit with parameters
  • Capture execution time and performance metrics
  • Integration with Koin's logging/monitoring API
@Monitor
class MyService {
    fun fetchData(): Data { ... }  // Calls will be intercepted
}

// Or on specific functions
class MyService {
    @Monitor
    fun fetchData(): Data { ... }  // Only this function intercepted
}

2.5 JSR-330 Compatibility Toggle

Allow enabling/disabling JSR-330 annotation support via Gradle property:

  • Add jsr330 option to koinCompiler { } DSL (default: true)
  • Pass option to compiler plugin via CommandLineProcessor
  • Skip jakarta.inject.* and javax.inject.* annotation processing when disabled
  • Document in README
// build.gradle.kts
koinCompiler {
    jsr330 = false  // Disable jakarta.inject/javax.inject support
}

2.6 Prebuilt Module Index

Generate pre-populated HashMap for instant startup — see PREBUILT_INDEX.md.

  • Design finalized (static KoinIndex, buildEntry, indexedModule)
  • koin-core API additions (KoinIndex, buildEntry, indexedModule, loadIndex)
  • Compiler: generate buildKoinIndex { buildEntry(...) } instead of module { buildSingle(...) }
  • Compiler: transform startKoin<T>() to use loadIndex() / map assignment
  • Unsafe DSL detection (hand-written lambdas → compiler error when prebuiltIndex = true)
  • prebuiltIndex Gradle option (default: false in 1.x, true in 2.x)

2.7 Property Validation (D): @Property/@PropertyValue Matching ✅

Validates that @Property("key") parameters have a corresponding @PropertyValue("key") default. Emits a warning (not error) since properties can be set at runtime via properties().

  • PropertyValueRegistry already collects @PropertyValue("key") registrations
  • BindingRegistry.validatePropertyKeys(): warns for @Property("key") without matching @PropertyValue
  • Called from CompileSafetyValidator.validate() after module validation
  • Box test: property_value_ok — @PropertyValue matches @Property key

2.8 @ScopeId Parameter Support ✅

Supports @ScopeId annotation for injecting values from a named Koin scope. Generates getScope("scopeId").get<T>(). Validation skips @ScopeId parameters.

  • KoinAnnotationFqNames.SCOPE_ID registered
  • QualifierExtractor.getScopeIdAnnotationName(): handles @ScopeId(MyScope::class) and @ScopeId(name = "id")
  • ParameterAnalyzer: classifies as isScopeId = true
  • KoinArgumentGenerator.createGetFromNamedScopeCall(): generates scope.getScope("id").get<T>()
  • Requirement.requiresValidation(): returns false for isScopeId
  • Box test: scope_id_ok
@Factory
class ProfileService(@ScopeId(name = "user_session") val session: UserSession)
// Generates: ProfileService(scope.getScope("user_session").get<UserSession>())

2.9 Scope Parameter Injection ✅

Constructor parameters of type org.koin.core.scope.Scope are injected with the scope receiver itself. Compile safety validation automatically skips Scope parameters (can't statically validate dynamic lookups).

  • KoinArgumentGenerator: detect param type org.koin.core.scope.Scope → pass scopeReceiver directly
  • ParameterAnalyzer: detect Scope type → skip validation (treated as implicitly @Provided)
  • Add tests (scope_param_ok box test)
@Scoped
class ScopedService(val scope: Scope) {
    fun dynamicLookup() = scope.get<SomeDep>()  // Not validated at compile time
}

Multi-Kotlin Version Compatibility (Deferred)

Current approach: Follow latest Kotlin versions and adapt as needed. No complex versioning scheme required.

Background: Kotlin compiler plugins are not binary compatible across minor versions. A plugin compiled with Kotlin 2.2.21 won't work with Kotlin 2.3.x. If needed in the future, version-aligned releases (0.1.0-kotlin-2.2.21) can be implemented.