Skip to content

Releases: InsertKoinIO/koin-compiler-plugin

1.1.0

Choose a tag to compare

@arnaudgiuliani arnaudgiuliani released this 29 Jul 14:13
5b3ccb3

A compile-safety architecture release: per-module validation is removed entirely in favor
of a single, authoritative full-graph check at each Koin entry point. This closes a real,
measured false-positive class, at the cost of leaf modules with no entry point of their own now
getting no compile-time safety diagnostics until something assembles a real graph around them.
Also ships incremental-compilation freshness hardening, allWarningsAsErrors compatibility, and a
collision-safe hint-file-naming scheme.

⚠️ Behavior change — per-module validation removed, full-graph validation only (#32, #51)

Why. A module validated in isolation cannot know how it will be wired into a larger app. This
stopped being theoretical: :core:notifications in a real playground app genuinely false-positived
on a dependency (PeerService) that a peer module provides — with no Gradle edge between the two,
the two are only unified downstream at the app's entry point. Per-module validation — checking a
module against its own definitions, its includes = [...], and its @Configuration siblings —
cannot see that far, and reported a hard KOIN-D001 for a dependency that resolves correctly once
the real app assembles both modules together. Rather than keep tuning the per-module oracle around
each new false-positive shape, per-module validation (and its "defer iff a provider exists
somewhere" oracle) is deleted outright. Full-graph validation — the check that runs at
startKoin/koinApplication/@KoinApplication — is now the sole compile-safety verifier.

What this means for you:

  • Rooted compiles (an app module with a real startKoin/koinApplication/@KoinApplication):
    more accurate. Genuine cross-module false positives like the peer-provider case above disappear;
    KOIN-D001 now always shows the real, assembled graph.
  • Leaf/library modules with no Koin entry point in their own compilation: KOIN-D001
    (missing dependency), KOIN-D004 (circular dependency), KOIN-D005/KOIN-D006 (parametersOf
    shape mismatches resolved via the graph), and KOIN-P001 (missing @PropertyValue) are now
    silent in that compilation — not because the module is safe, but because compile-time cannot
    know how it will be assembled downstream. The graph is still checked, correctly, at the real
    entry point once one exists in the compilation. This is disclosed via a default-visible
    (INFO-severity) message rather than failing silently; see logSeverity below to control its
    visibility.
  • KOIN-W002 (the old "deferred, no provider hint found anywhere" warning) is deleted — there
    is no more deferral machinery to warn about.
  • Circular-dependency detection (KOIN-D004) going silent for a leaf module is intentional, not a
    regression: detecting a cycle requires seeing the whole graph, and a same-module-only check was
    never a complete cycle detector even under the old per-module validation (it only ever saw
    local/sibling visibility).

Full account, including the design docs this reverses: docs/COMPILE_SAFETY_A3_PLAN.md
(superseded-banner) and docs/COMPILE_TIME_SAFETY.md.

🐛 Fixes

Orphaned @Module classes were silently treated as reachable (found during this release's own verification)

A plain @Module @ComponentScan(...) class with no @Configuration and not referenced by anyone's
includes = [...] was silently treated as part of the graph anyway, as long as the entry point used
a bare/default-labeled @KoinApplication/startKoin — the overwhelmingly common case. Its
@ComponentScan-discovered definitions (including cross-module ones) were folded into the resolved
graph and validated as satisfied, when the actual generated module tree never wired them in at all:
build green, runtime crash. Root cause: the entry-point module-discovery step accidentally called a
label-reader meant for the entry-point class's @KoinApplication(configurations=[...]) argument
against module classes, which never carry that annotation — so it always hit that reader's
"annotation absent" fallback (["default"]), making any @Module class match. This bug predates
1.1.0 (traced to 1.0.0-GA1) but was masked by the old per-module validation, which used to
validate each such module in isolation too; removing it made this bug load-bearing. Fixed, with a
regression test
(entry_orphan_module_not_reachable_d001) proving an orphaned module without @Configuration or an
includes edge is now correctly excluded from the graph.

KOIN-D001 now names the real culprit module and source location

Missing-dependency errors now carry file:line for the failing definition and the actual owning
module's name (previously degraded to a generic app/root label once every KOIN-D001 funnels
through the one remaining full-graph check). Also fixed: attribution for FunctionDef-shaped definitions
used a bare simple name, which could collide across same-named modules in different packages — now
uses the fully-qualified name.

KOIN-D001 deduplication across multiple entry points

A module reachable from more than one startKoin/koinApplication/@KoinApplication in the same
compilation (common in test-apps: ~9 entry points is typical) previously re-validated and re-emitted
the same missing-dependency error once per entry point. Now deduplicated by (definition, missing
requirement), so a shared module with one real problem reports it exactly once.

D005/D006 (parametersOf shape checks) no longer require a Koin entry point

The parametersOf(...) argument-count/presence check is graph-independent — the expected slots come
from the target's own constructor, not from an assembled graph — so it now runs unconditionally
instead of being skipped whenever no entry point is present in the compilation, matching its actual
data dependency. KOIN-D002 (call-site resolution) correctly keeps requiring an assembled graph
and stays silent without one — the two diagnostics no longer share a gate they don't share a
dependency on.

Cross-module qualifier and typed-scope resolution verified under the new sole-verifier design

New regression coverage confirms full-graph validation matches @Named qualifiers and typed
@Scope(X::class) keys correctly across Gradle module boundaries, not just "some provider of this
type exists somewhere" — this matters more now that there's no per-module fallback to catch a wrong
match.

Known pre-existing limitation, found while writing this coverage (not new, not fixed this
release):
BindingRegistry.findProvider's scope-visibility check only matches a typed
@Scope(X::class); a named @Scope(name = "...") provider has no scopeClass and is treated
as visible everywhere regardless of name.

🔒 Incremental-compilation freshness

Removing per-module leaf-local checking made full-graph validation's own freshness across
incremental (IC) rebuilds load-bearing in a way it wasn't before — these changes close that gap:

  • strictSafety is now mandatory once an aggregator is auto-detected, not opt-in. Previously,
    an explicit strictSafety = false silently won over the plugin's own startKoin/
    koinApplication/@KoinApplication detection, letting an aggregator's compileKotlin stay
    cacheable/up-to-date even when the DI graph changed underneath it (lambda-body DSL edits and
    new @ComponentScan-covered files don't register as ABI changes IC can see). strictSafety = true
    still works everywhere; the new escape hatch for a genuine detector misfire (the marker appears
    only in a comment/string, not a real entry point) is strictSafetyForceOff = true — a separate,
    explicit acknowledgement from a plain false.
  • Extended IC tracker linking: KoinDSLTransformer's 5 DSL definition call sites now register
    with ExpectActualTracker (alongside the existing LookupTracker calls), matching the pairing
    KoinAnnotationProcessor/KoinStartTransformer already had — closes another source of stale
    incremental state around DSL hint files.
  • A theorized @ComponentScan new-file freshness gap did not reproduce: adding a new
    @Singleton/@Factory class to a scanned package is itself a source-set input change, which
    Gradle already invalidates the owning module's compileKotlin task for, independent of anything
    Koin-specific — verified live on a real playground app. No plugin-side fix was needed here.
  • Known limitation, unchanged by this release: a module going completely empty (its last
    includes() or its last local definition removed, with nothing replacing it) is not detected
    incrementally without a full clean + --no-build-cache. This is a K2-internals residual (a
    keep-alive hint's signature not being re-resolved within one IC session), not a missing source
    edge — see playground-apps/README.md's "Known limitation" note.

🔇 allWarningsAsErrors / -Werror compatibility (#73)

Informational plugin output (userLogs/debugLogs messages, the @Monitor-tracing-enabled
summary) was emitted at WARNING severity unconditionally, which fails a build compiled with
allWarningsAsErrors even though none of it is a real diagnostic.

  • New logSeverity option ("warning" default, or "info") covers all of the above.
  • New, separate versionCheckSeverity option covers only the Kotlin-version-compatibility
    warning ("you're on an unverified Kotlin version") — kept independent because muting informational
    noise shouldn't also silence a real compiler-compatibility risk; set it to "info" only after
    assessing that risk yourself.
  • Real diagnostics (KOIN-Dxxx/KOIN-Wxxx/etc.) are unaffected by either setting — they always
    report at their own severity.
koinCompiler {
    logSeverity = "info"           // downgrade informational output, default "warning"
    versionCheckSeverity = "info"  // downgrade the version-compatibility check, default "warning"
}

🧷 Hint-file collision sa...

Read more

1.0.2

Choose a tag to compare

@arnaudgiuliani arnaudgiuliani released this 10 Jul 13:28

A correctness-focused maintenance release: it removes several false compile-safety errors in multi-module projects, fixes duplicate-hint KLIB failures on iOS/Native/WASM-JS, and hardens per-compilation state under parallel Gradle daemons.

🔑 Highlights

  • No more false "missing dependency" errors across modules — a @Module whose dependency is provided by a sibling module (assembled only at @KoinApplication / startKoin) no longer fails with KOIN-D001. It defers to the entry-point graph, and surfaces a new KOIN-W002 warning only when no complete closure is visible in the compilation (#51).
  • iOS / Native / WASM-JS build reliability — cross-module @ComponentScan and cross-module top-level @Single functions no longer emit duplicate hint declarations that broke KLIB serialization (#62).
  • Fewer false compile-safety errors on DSL code — typed DSL definitions with custom lambdas, indirect parametersOf helpers, and outer DSL qualifiers are now understood (#36, #49, #61, #41).
  • Compose entry point validatedKoinApplication(configuration = koinConfiguration { … }) now runs full-graph safety (#38).

🐛 Fixes

False KOIN-D001 for cross-module (sibling) dependencies — #51 (KTZ-4256)

In a layered multi-module build, a @Module is compiled without visibility of the sibling modules a downstream @KoinApplication(modules = […]) assembles alongside it. Per-module (A2) validation therefore reported a provider that lives in a sibling as a hard KOIN-D001 missing dependency. Validation now defers an unresolved binding when a provider hint for the type exists elsewhere on the build graph, settling it authoritatively at the entry-point closure (A3) or at runtime checkModules(). When no complete closure is present in the compilation (e.g. a leaf library module), it emits the new KOIN-W002 warning instead of an error.

Scope: this narrows the false positive to the common shape (provider is a compile dependency, or compiled alongside the consumer). A genuine missing dependency with no provider hint anywhere is still a hard KOIN-D001. A provider that lives in a non-dependency peer module (type declared in a shared module) is not yet distinguishable at the leaf and may still report — full A2 relaxation is planned for 1.1.

Duplicate hint declarations broke iOS / Native / WASM-JS — #62 (KTZ-4365)

A cross-module @ComponentScan covering a dependency module's package, and cross-module top-level @Single functions, could register the same definition more than once — emitting duplicate componentscan_* / definition_function_* hint declarations. The JVM/DEX toolchain tolerated it (a D8 "multiple definitions" warning); KLIB serialization (iOS/Native/WASM-JS) rejected it with a hard SignatureClashDetector error. Definitions are now de-duplicated by class identity (and by type+qualifier for functions), so each is emitted exactly once per target.

False KOIN-D001 for typed DSL definitions with non-create lambdas — #36, #49

single<T> { existingInstance }, single<T> { provideX() }, viewModel { VM() } and similar shapes are now recognized as providing T, so compile-safety no longer reports T as a missing definition. The user's lambda is left untouched.

False KOIN-D006 for indirect parametersOf helpers — #61

A call site passing an opaque params lambda (e.g. { buildParams() }) no longer triggers KOIN-D006 ("forgot parametersOf"). The diagnostic now fires only when no params lambda is present at all.

Qualifier lost on DSL create definitions — #41

An outer DSL qualifier (single<T>(named("x")) { create(::T) }) is now propagated into the compile-safety hints, so qualified cross-module definitions resolve correctly instead of producing spurious mismatches.

Compose koinConfiguration { } entry point not validated — #38

KoinApplication(configuration = koinConfiguration { modules(…) }) is now recognized as a Koin entry point, enabling full-graph (A3) compile-safety for Compose apps. The koinConfiguration call is only marked as an entry point — it is not rewritten, so runtime behavior is unchanged.

Flaky / order-dependent behavior under parallel Gradle daemons — (KTZ-4414)

Plugin config flags and the @PropertyValue registry were held in process-global mutable state shared across every compilation in a Gradle daemon. Parallel or interleaved compilations could read another build's flags or have a @PropertyValue default dropped. State is now held per-compilation (thread-local, rebound onto the IR phase), matching the existing per-compilation message-collector handling.

⚠️ Auto-binding excludes framework/marker supertypes — #43, #64

This changes generated code and can affect runtime resolution. Auto-detected bindings no longer include framework plumbing / marker supertypes: kotlin.Any, org.koin.core.component.KoinComponent, KoinScopeComponent, and androidx.lifecycle.ViewModel / AndroidViewModel. Previously a @KoinViewModel / @Single class implementing one of these could be auto-bound to the framework base type, letting get<ViewModel>() / get<KoinComponent>() resolve to an arbitrary component (silent wrong-instance resolution). A definition is now registered under its own type and its genuine domain interfaces only.

Explicit bindings are unaffected@Single(binds = [ViewModel::class]) still binds exactly what you list. If you relied on auto-binding to one of the excluded supertypes, add it explicitly with binds = [...].

✅ Compatibility

Kotlin 2.3.20 Kotlin 2.4.0
JVM / Android
iOS / Native
WASM/JS — DSL
WASM/JS — annotations ⚠️ KT-82395
  • Koin: 4.2.0+

📦 Install

plugins {
    id("io.insert-koin.compiler.plugin") version "1.0.2"
}

Full changelog: 1.0.1...1.0.2

1.0.1

Choose a tag to compare

@arnaudgiuliani arnaudgiuliani released this 12 Jun 07:41
5a3910d

A maintenance release focused on Kotlin 2.4.0 support and Kotlin/Native + WASM/JS build reliability. One plugin artifact now spans Kotlin 2.3.20 → 2.4.x.

🔑 Highlights

  • Kotlin 2.4.0 support — the plugin no longer crashes on Kotlin 2.4.0, and the same artifact also works on Kotlin 2.3.20.
  • iOS / Native / WASM builds fixed — annotation definitions no longer break KLIB serialization.
  • @Single(createdAtStart = true) honored on definition functions — eager singletons are created at startKoin again.

🐛 Fixes

Kotlin version compatibility — #19, #42 (and koin#2431)

The plugin hard-crashed on the two most recent Kotlin versions:

  • Kotlin 2.4.0ClassCastException during FIR extension registration.
  • Kotlin 2.3.20NoSuchMethodError (IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB).

1.0.1 introduces a Kotlin version-adapter layer: the core is compiled against the stable IR API, and a small per-version adapter (selected at compile time) absorbs the breaking compiler-internal differences. A single published jar supports Kotlin 2.3.20 and 2.4.0; an unrecognized newer Kotlin gets a warning and best-effort support, while versions below the floor get a clear, actionable error instead of an internal crash.

Duplicate injectedparams_* signatures broke iOS + WASM/JS — #44, #40

A type collected by more than one @ComponentScan module generated the @InjectedParam hint function twice with identical signatures. The JVM tolerated it; KLIB serialization (iOS/Native/WASM/JS) rejected it with "Different declarations with the same signatures". The hint is now emitted exactly once per target. (Regression vs 1.0.0-RC2.)

WASM/JS KLIB serialization — #40

The plugin's generated hint files lacked a resolvable source, failing the JS/WASM KLIB serializer ("No file found for source null"). Fixed — DSL-based projects now build on WASM/JS.

@Single(createdAtStart = true) silently dropped — koin#2425, koin#2415

createdAtStart = true on a @Single / @Singleton definition function inside a @Module was discarded in codegen, so eager singletons were never created at startKoin (no error or warning). Now propagated correctly. (The @Module(createdAtStart) and @Singleton class cases were already fixed in 1.0.0-RC3.13.)

⚠️ Known limitation — annotation-based WASM/JS on Kotlin 2.3.20

Annotation-based projects (@Module / @ComponentScan) targeting WASM/JS require Kotlin 2.4.0. On Kotlin 2.3.20 they hit an upstream Kotlin compiler bug — KT-82395 — in the JS/WASM KLIB metadata serializer, which the plugin cannot work around. Kotlin 2.4.0 resolves it. (iOS/Native and DSL-based WASM/JS are unaffected and work on both Kotlin versions.)

✅ Compatibility

Kotlin 2.3.20 Kotlin 2.4.0
JVM / Android
iOS / Native
WASM/JS — DSL
WASM/JS — annotations ⚠️ KT-82395

📦 Install

plugins {
    id("io.insert-koin.compiler.plugin") version "1.0.1"
}

Full changelog: 1.0.0...1.0.1

1.0.0

Choose a tag to compare

@arnaudgiuliani arnaudgiuliani released this 20 May 07:59

Koin Compiler Plugin 1.0.0

The first stable release. RC1 shipped in April 2026; over the following weeks the safety pass, incremental-compilation handling, and K2.3.20 compatibility came together. The compiler-plugin path is now ready for production: full-graph compile-time safety, cross-module call-site validation, IC-aware safe-defaults, and K2.3.20 support.

Requires: Kotlin 2.3.20+ (K2) | Koin 4.2.1+


What's New Since 1.0.0-RC2

Compile-time safety expanded (A2/A3/A4)

  • Circular dependency detection — cycles like A → B → A are caught during graph traversal in A2/A3, no longer waiting for runtime.
  • Call-site validation with dynamic parametersget<T> { parametersOf(...) } and ViewModel/inject sites with parametersOf {} flows are validated against the assembled graph.
  • Compose-wrapped lambdas — A4 traverses Compose lambdas, so koinViewModel<T>() inside @Composable builders is validated like any other call site.
  • Qualifier-collision guard — A4 reports when two definitions resolve to the same (raw class + qualifier) key, preventing silent last-wins overrides at runtime.
  • KOIN-D007@Factory returning a type that extends a suspend fun interface is now blocked at compile time (previously crashed Fir2Ir).
  • Bare koinConfiguration / koinApplication / startKoin no longer bypass A3 — the full-graph pass runs even without an explicit <T> type parameter.
  • Cross-module DSL visibility under typed startKoin<T>() — A4 now sees DSL module { … } definitions declared in dependency JARs when the aggregator uses the typed entry point.

Incremental compilation & build robustness

  • strictSafety flag — auto-enabled on modules that contain startKoin, koinApplication, or @KoinApplication. Forces the full-graph safety pass to re-run on the aggregator each build, working around two K2 IC gaps: DSL changes inside module { } lambda bodies (not part of any declaration's ABI) and @ComponentScan package-scope discovery (no source-level edge). Library and feature modules stay fully incremental.
  • Module-disambiguated hint file names — stable anchors prevent cross-module Hints.kt collisions during multi-platform builds.
  • kapt / Hilt coexistence — defensive guard around KtPsiSourceElement.psi prevents Fir2Ir crashes when the plugin runs alongside kapt or Hilt during migration.

Kotlin 2.3.20 compatibility

  • Fir2Ir crash on K2.3.20HINTS_PACKAGE is now claimed unconditionally, fixing a regression when building against the latest K2.

Annotation fixes

  • @Module(createdAtStart = true) — was silently ignored; now correctly forwards the flag to the generated module { }.
  • @Scope(name = "…") — previously produced no bean definition; the scope DSL is now generated as expected.
  • @ScopeId(name = "…") — resolution behaviour locked in via regression coverage.

Diagnostics

  • CTA banner ordering — error reports anchor on the last error in the chain and use a per-extension collector, so the actionable hint is always closest to the offending call site.
  • Compiler error info — error frames now embed the diagnostic code and a single-line "how to fix" pointer.

Performance

  • Memoized startKoin module discovery — repeated scans during a single compilation reuse the resolved module set.
  • Indexed @ComponentScan filtering — package-scope discovery now uses an indexed lookup instead of repeated linear scans, noticeably faster on large multi-module projects.

Repo / docs

  • Playground apps moved into the main repo (playground-apps/app-dsl, playground-apps/app-annotations) — single clone, single build, no separate repo to keep in sync.
  • Documentation updatesstrictSafety, K2.3.20 minimum, circular-dep detection at compile time, KOIN-D007.

Behaviour changes to note when upgrading

strictSafety is on by default on aggregator modules. If your app contains startKoin, koinApplication, or @KoinApplication, that module's compileKotlin task will re-run the A3 safety pass on every build (library/feature modules unaffected). The cost is bounded, and it closes the IC gaps that previously let graph changes slip through cached builds. Set koinCompiler { strictSafety = false } to opt out.

Kotlin minimum bumped to 2.3.20 (was 2.3.0) — the Fir2Ir fix requires the newer compiler. If you're pinned to 2.3.0, stay on 1.0.0-RC2 until you can upgrade.


Closed issues since the project went public

Fixed

  • #1 — FileAnalysisException during compilation (@FatalCatharsis)
  • #2 — Unrecognized @ScopeId (@limuyang2)
  • #3 — Use a stable Kotlin version (@adamglin0)
  • #7@Provided still flagging missing dependencies (@kmbisset89)
  • #8 — Not generating a module (@kmbisset89)
  • #11 — Incomplete IR type generation for Scope.get() (@alex28sh)
  • #12 — Runtime stack overflow with Kotlin delegation pattern (@pupava)
  • #14 — Generated .module() extension not recognized by IntelliJ (@DenAbr)
  • #15 — Cannot fetch 0.6.1 version (@FSBlocks)
  • #16 — Compiler crash on @Factory returning fun interface extending suspend function type (@krzdabrowski) — now also blocked diagnostically via KOIN-D007
  • #17 — Incompatibility with Arrow core (@gael-ft)
  • #18 — Compile-time safety breaks Native builds with generic types (@alex-z0)
  • #20 — Duplicate callsite hint classes in multi-module builds (@norbertsitko)
  • #22 — Compiler plugin misses cross-module bindings from @Single(binds = [...]) provider function (@flaringapp)
  • #24 — IR crash on generic ViewModel base class on Kotlin/Native (iOS) (@hmy65)
  • #29 — FileAnalysisException when migrating from Hilt (@theimpulson)
  • #32compileSafety missing binding silently passes on incremental compilation (@j-bajon) — drove the strictSafety design
  • #34@Scope(name=…) + @Scoped(binds=[…]) silently produce no bean definitions (@rduriancik)
  • #35 — Module doesn't contain package org.koin.plugin.hints (@0xMatthewGroves)
  • #38compileSafety checks bypassed when using KoinApplication Composable in CMP (@rajdeepvaghela)

Won't fix / deferred

  • #21 — IDE cannot resolve generated module() extension (@JordanLongstaff) — documented as a known limitation pending IDE-side support
  • #33 — Feature request: wasmJs target support (@dmitry-stakhov) — tracked for a future release

Cross-repo fixes

  • koin#2368@ScopeId(name = "…") resolution behaviour
  • koin#2380 — typed startKoin<T> + @KoinApplication(modules=[…]) + scanned @KoinViewModel
  • koin#2400 — nested DSL includes(...) reachability
  • koin#2402 — explicit @KoinApplication(modules = [...]) overrides discovered @Configuration

Contributors

Project lead: @arnaudgiuliani (Arnaud Giuliani)

Code contributions (commit authors and merged PRs across all releases):

Read more

1.0.0-RC2

1.0.0-RC2 Pre-release
Pre-release

Choose a tag to compare

@arnaudgiuliani arnaudgiuliani released this 22 Apr 09:50

Koin Compiler Plugin 1.0.0-RC2

Stability pass on top of RC1: fixes user-reported crashes on iOS/Native, multi-module dex-merge collisions, and a module load-order behaviour change that makes app-level overrides actually win.

Requires: Kotlin 2.3.x+ (K2) | Koin 4.2.1+


What's New Since 1.0.0-RC1

Cross-module visibility

  • @Module + @ComponentScan without @Configuration now emits cross-module scan hints — previously scan-covered definitions were invisible to downstream compileSafety unless the module was also @Configuration. See PR #25.
  • @Single(binds = [...]) on @Module provider functions preserves binding metadata across module boundaries — consumers can now resolve interfaces bound via provider functions in a dependency JAR. See PR #23 / #22.

Fixes

  • Module load order (koin#2402) — auto-discovered @Configuration modules now load first and explicit @KoinApplication(modules = [...]) load last, so app-level overrides win over dependency defaults under Koin's last-wins semantics.
  • Duplicate call-site hint classes in multi-module Android builds (#20) — hint filenames are now prefixed with the compilation module identifier; no more org.koin.plugin.hints.XxxCallsiteKt collisions at dex merge.
  • Generic DSL types on iOS/Native (#18) — Kotlin/Native klib signature mangler no longer crashes on generic DSL types (single<Navigator<Key>>()). Hint emission erases type arguments to match runtime Koin's erasure behaviour.
  • Arrow Raise.bind() / ktor resourceScope { bind() } crash (#17) — collectBindType now matches Koin's bind by FqName, no longer intercepting unrelated library bind functions.
  • single<T> { create(::Impl) } with interface type parameter — the outer <T> is now registered as the provided type (previously Impl), so koin.get<Interface>() compile-safety passes as expected.
  • Missing DSL-artifact error (RC2.3) — @KoinViewModel / @KoinWorker without their runtime artifact (koin-core-viewmodel / koin-android-workmanager) now fails the build with a clear message pointing at the missing dependency, instead of silently producing a broken definition.
  • Unit-returning top-level @Singleton functions (RC2.2) — klib signature-clash fix; two qualified side-effect initializers no longer collide on iOS/JS/Wasm.
  • Custom qualifier annotations (RC2.1) — plain custom @Qualifier annotations now produce TypeQualifier, matching runtime named<T>() / typeQualifier<T>() semantics.

Documentation

Extended docs on module load order, generic DSL types (with the named<T>() qualifier pattern used by koin-compose-navigation3), and troubleshooting the new missing-artifact compile error.


Behaviour change to note when upgrading

Module load order: if your app declares @KoinApplication(modules = [AppModule::class]) and AppModule defines a binding that is also defined in a @Configuration-discovered dependency module, the app's binding now wins (previously, the dep overrode the app). This is the documented last-wins semantic applied correctly — but if you were relying on the previous behaviour, reorder via an explicit modules = [Dep::class, AppModule::class] list to control load order precisely.


Contributors

Code contributions this release:

Ongoing contributors (carried over from RC1):

  • Arnaud Giuliani — project lead
  • @JellyBrick — caching optimisations (PR #5), Gradle release signing fix
  • Kevin Chiu — Gradle plugin package fix
  • Youssef Shoaib — runtime annotations provider, build improvements

Issue reporters — the reproductions were excellent, thank you:

And Francois Dabonot (Kotzilla Slack), whose migration feedback drove the missing-artifact compile error introduced in RC2.3.


Full changelog

1.0.0-RC1

1.0.0-RC1 Pre-release
Pre-release

Choose a tag to compare

@arnaudgiuliani arnaudgiuliani released this 10 Apr 14:27

Koin Compiler Plugin 1.0.0-RC1

A native Kotlin Compiler Plugin for Koin dependency injection. Inline compile-time transformations — no generated Kotlin files to manage.

Replaces Koin Annotations (KSP) with a K2 compiler plugin that transforms DSL calls and processes annotations directly in the FIR/IR pipeline. Full Kotlin Multiplatform support.

Requires: Kotlin 2.3.x+ (K2) | Koin 4.2.1+


Features

DSL Transformations

Write idiomatic Koin DSL — the plugin resolves constructors at compile time.

DSL Call Description
single<T>(), factory<T>(), viewModel<T>(), worker<T>(), scoped<T>() Definition with automatic constructor resolution
create(::T) Constructor reference in scopes
startKoin<T>(), koinApplication<T>(), module<T>() Application and module loading
modules(vararg KClass) Multi-module loading

Annotations

Full annotation-driven DI as an alternative (or complement) to DSL.

Definitions — on classes, @Module functions, or top-level functions:
@Singleton, @Factory, @Scoped, @KoinViewModel, @KoinWorker

Modules:
@Module, @ComponentScan, @Configuration (with label-based grouping)

Parameters:
@Named, @Qualifier (string and type), @InjectedParam, @Property, @PropertyValue, @ScopeId, @Provided

Compile-Time Safety

Detect missing or mismatched dependencies at build time — not at runtime.

Level Scope
A1 Per-module: local definitions + explicit includes
A2 Configuration group: @Configuration siblings share definitions
A3 Full graph: all modules assembled via startKoin<T>()
A4 Call-site: get<T>(), inject<T>(), koinViewModel<T>()
B DSL modules: single<T>(), factory<T>() in hand-written modules
C Cross-module: definitions from dependency JARs via hint functions
D Properties: @Property/@PropertyValue key matching

Automatically skipped: nullable params, @InjectedParam, @Provided, @ScopeId, Scope params, default values, List<T>, Android framework types (Context, Application, SavedStateHandle, etc.)

Cross-Module Discovery

Definitions, qualifiers, scopes, and bindings propagate across Gradle modules via lightweight hint functions. No runtime reflection, no classpath scanning.

Kotlin Multiplatform

Full KMP support — JVM, JS, Wasm, Native. Dramatically simplified setup compared to the KSP-based approach (no per-target wiring).


What's New in 1.0.0-RC1

Since 0.6.2:

  • Fixed missing binding detection during compile-time safety validation

Since 0.4.x:

  • @ScopeId — inject dependencies from named or typed scopes
  • Scope parameter injection — pass the Koin scope receiver directly
  • @PropertyValue/@Property validation with warnings for missing defaults
  • @Provided annotation — mark types as externally available, skip safety validation
  • module<T>() and modules(vararg KClass) DSL interception
  • binds=[] respected to suppress auto-binding (#12)
  • Actualized IR return types for Wasm/JS targets (#11)
  • Performance: cached referenceFunctions, batched hint file generation

Getting Started

// build.gradle.kts
plugins {
    id("io.insert-koin.compiler.plugin") version "1.0.0-RC1"
}

koinCompiler {
    compileSafety = true        // Compile-time dependency validation (default)
    skipDefaultValues = true    // Use Kotlin defaults instead of DI resolution (default)
    unsafeDslChecks = true      // Validate create() lambda safety (default)
    userLogs = true             // Component detection logs
    debugLogs = false           // Verbose internal logs
}

Migrating from Koin Annotations (KSP)

  1. Replace ksp("io.insert-koin:koin-ksp-compiler:...") with the Gradle plugin above
  2. Remove ksp { } configuration blocks
  3. Delete generated *Module.kt files — the plugin transforms inline
  4. See the full Migration Guide for details

Contributors

  • Arnaud Giuliani — project lead
  • JellyBrick — caching optimizations (PR #5), Gradle release signing fix
  • Kevin Chiu — Gradle plugin package fix
  • Youssef Shoaib — runtime annotations provider, build improvements

0.6.2

Choose a tag to compare

@arnaudgiuliani arnaudgiuliani released this 03 Apr 17:16

Koin Compiler Plugin 0.6.2

Compatibility: Kotlin 2.3.20 · Koin 4.2.1-RC1

Bug Fixes

  • Fix #11 — WASM/JS type actualization — Actualize IR return types for generic Koin calls (Scope.get<T>(), getOrNull<T>(), inject<T>(), getAll<T>(), ParametersHolder.get<T>()). Prevents unbound IrTypeParameterSymbolImpl crashes on WASM/JS/Native targets. (3d9d2d8)

  • Fix #12 — Delegation pattern auto-binding — Respect binds = [] to suppress auto-binding. Classes using the delegation pattern (class Decorated(val delegate: MyService) : MyService) with @Singleton(binds = []) no longer cause recursive resolution stack overflows. (78f477c)

Performance

  • Cache referenceFunctions() lookups — Avoid repeated expensive compiler API calls across modules. Same CallableId queried N times → 1 real lookup + (N-1) O(1) cache hits.
  • Cache @Configuration module discoverydiscoverConfigurationModulesFromHints() results cached by label set. 10 modules with same labels → 1 discovery instead of 10.
  • Cache modulesByFqName map — Built once, reused across all module validations.
  • Batch hint IrFile creation — One IrFile per module instead of per definition, reducing synthetic file count from O(definitions) to O(modules).

Caching optimizations inspired by @JellyBrick (PR #5) 🙏

Compile Safety

  • Phase 3.6 guard — Cross-module call-site hint validation now only runs when a full graph has been assembled (via @KoinApplication). Prevents false positives in library modules without full graph visibility.

Build

  • Gradle signing fix — Only apply signing plugin when IS_RELEASE is set (PR #4).

0.6.1

0.6.1 Pre-release
Pre-release

Choose a tag to compare

@arnaudgiuliani arnaudgiuliani released this 30 Mar 16:56

Koin Compiler Plugin 0.6.1

Release date: 2026-03-30

New Features

module<T>() and modules(vararg KClass) APIs

Load @Module classes without referencing generated code directly. The compiler plugin intercepts these calls and transforms them at compile time. (#14)

startKoin {
    module<NetworkModule>()
    modules(DataModule::class, CacheModule::class)
}

Requires Koin 4.2.1-beta-1+

@ScopeId Parameter Support

Resolve dependencies from named Koin scopes. Generates getScope("id").get<T>(). Compile safety skips @ScopeId parameters. (#2)

@Factory
class ProfileService(@ScopeId(name = "user_session") val session: UserSession)
// Generates: ProfileService(scope.getScope("user_session").get())

@Provided on Parameters

Previously only worked on classes. Now also works on individual constructor parameters to skip compile safety for that specific parameter. (#7)

@Singleton
class MyService(@Provided val ctx: PlatformContext)

Scope Parameter Injection

Parameters of type org.koin.core.scope.Scope are automatically injected with the scope receiver. No annotation needed.

@Scoped
class ScopedService(val scope: Scope)
// Generates: ScopedService(scope)

@Property/@PropertyValue Validation

Warns at compile time when @Property("key") has no matching @PropertyValue("key") default.

@PropertyValue("api.timeout")
val defaultTimeout = 30

@Factory
class ApiClient(@Property("api.timeout") val timeout: Int)  // OK

@Factory
class Other(@Property("missing.key") val value: String)     // WARNING

DSL Compile Safety Improvements

  • Module reachability validation — Tracks which DSL modules are loaded via modules() and includes(). Reports compile errors for definitions in unreachable modules.
  • bind() operator support — Explicit bind(Interface::class) is now tracked for DSL definitions. Auto-binding of supertypes removed for DSL path (matches Koin runtime behavior).
  • create(::function) hints — Provider-only definitions from create(::function) now generate cross-module hints with providerOnly flag.
  • Qualifier propagation in DSL hints@Named, @Qualifier, and type qualifiers are now encoded in DSL cross-module hints.
  • Call-site detectionby inject() and by viewModel() property delegates in class bodies are now detected for A4 validation.

Bug Fixes

  • Fix qualifier propagation in function definition calls
  • Fix @Monitor tracing: warn if Kotzilla SDK library is missing
  • Fix DSL bind() not being tracked — removing bind now correctly triggers compile error
  • Fix create(::function) not producing cross-module DSL hints
  • Fix FIR module data null for external library types (e.g., DataStore, CoroutineDispatcher)
  • Fix cross-module qualifier encoding with dots in names

Breaking Changes

  • DSL auto-binding removed — DSL definitions (single<T>(), factory<T>()) no longer auto-bind to supertypes. Use explicit bind(Interface::class) to register secondary types. This matches Koin runtime behavior.

Compatibility

Dependency Version
Koin 4.2.1-beta-1+ (for module<T>() API), 4.2.0-RC2+ (for other features)
Kotlin 2.3.x+ (K2 compiler required)

Resolved Issues

  • #2@ScopeId unrecognized
  • #7@Provided still flagging missing dependencies
  • #14 — Generated .module() extension not recognized by IntelliJ (mitigated with module<T>() API)

0.4.0

0.4.0 Pre-release
Pre-release

Choose a tag to compare

@arnaudgiuliani arnaudgiuliani released this 12 Mar 17:08

Native Kotlin Compiler Plugin for Koin — K2 Required (Kotlin 2.3.x+) | Koin 4.2.0-RC2+

Highlights

Compile-Time Dependency Safety is the headline feature of 0.4.0. The plugin now validates your entire dependency graph at compile time, catching missing bindings before they become runtime crashes.

New Features

Compile-Time Safety Checks (compileSafety = true, on by default)

Multi-layered validation that progressively widens visibility:

  • A1 — Per-Module: validates definitions within a single @module plus its explicit includes
  • A2 — Configuration Groups: modules sharing a @configuration label are validated together
  • A3 — Full Graph (startKoin): validates the complete assembled graph when using @KoinApplication
  • A4 — Call-Site Validation: checks get(), inject(), and koinViewModel() call sites against the known graph
  • B — DSL Definitions: single(), factory(), etc. participate in the safety graph alongside annotation-based definitions
  • C — Cross-Gradle-Module: definitions from dependency JARs are discovered via hint functions
  • C2 — Full Hint Metadata: cross-module function hints now carry qualifier, scope, and binding information

Catches missing non-nullable dependencies, unresolved Lazy, qualifier mismatches, and cross-scope errors at compile time. Nullable params, @InjectedParam, @Property, List, and defaulted
parameters are safely skipped.

@provided Annotation

Mark types as externally available at runtime (e.g., platform types not declared as Koin definitions). Safety checks will skip them.

  @Provided
  class ExternalService  // provided by framework at runtime

  @Singleton
  class MyService(val ext: ExternalService)  // no error

Android Framework Whitelist

Common Android types are automatically whitelisted — no @provided needed:
Context, Activity, Application, Fragment, SavedStateHandle, WorkerParameters

@monitor Annotation

Function interception for logging and performance monitoring:

  @Monitor
  class MyService {
      fun fetchData(): Data { ... }  // calls intercepted with timing/logging
  }

skipDefaultValues Option (default: true)

Parameters with Kotlin default values are no longer injected from the DI container by default. Annotated and nullable parameters are still resolved normally.

  class Service(val a: A, val name: String = "default")
  single<Service>()
  // Generated: Service(scope.get())  — name uses Kotlin default

Incremental Compilation Support

Dirty marker / IC recompilation detection ensures the plugin cooperates correctly with Kotlin's incremental compilation.

KMP Improvements

  • Full hint function generation for JVM, JS, and Wasm targets
  • @deprecated(HIDDEN) on generated hints to avoid polluting IDE autocomplete
  • Fix for @configuration and @componentscan detection in multi-target builds
  • KLIB workaround extended to JS and Wasm targets

Configuration

  koinCompiler {
      compileSafety = true       // Compile-time dependency validation (default: true)
      unsafeDslChecks = true     // Validate create() is sole lambda instruction (default: true)
      skipDefaultValues = true   // Skip injection for defaulted params (default: true)
      userLogs = true            // Component detection logs
      debugLogs = true           // Verbose internal logs
  }

Compatibility

  • Kotlin: 2.3.x+ (K2 compiler required)
  • Koin: 4.2.0-RC2+