{"id":30697448,"url":"https://github.com/a-sit-plus/modulator","last_synced_at":"2025-09-02T09:09:20.321Z","repository":{"id":311892137,"uuid":"1044520994","full_name":"a-sit-plus/modulator","owner":"a-sit-plus","description":"CompileOnly Kotlin Multiplatform Dependencies without Dependency Hell","archived":false,"fork":false,"pushed_at":"2025-08-27T08:47:46.000Z","size":609,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-27T17:32:56.455Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/a-sit-plus.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-25T20:11:16.000Z","updated_at":"2025-08-27T10:02:00.000Z","dependencies_parsed_at":"2025-08-27T17:37:38.991Z","dependency_job_id":"10588c17-46e1-45a0-a5ac-43d3acc32a23","html_url":"https://github.com/a-sit-plus/modulator","commit_stats":null,"previous_names":["a-sit-plus/modulator"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/a-sit-plus/modulator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-sit-plus%2Fmodulator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-sit-plus%2Fmodulator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-sit-plus%2Fmodulator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-sit-plus%2Fmodulator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/a-sit-plus","download_url":"https://codeload.github.com/a-sit-plus/modulator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-sit-plus%2Fmodulator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273258181,"owners_count":25073625,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-02T02:00:09.530Z","response_time":77,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-09-02T09:09:18.711Z","updated_at":"2025-09-02T09:09:20.312Z","avatar_url":"https://github.com/a-sit-plus.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg alt=\"modulator – compileOnly Kotlin Multiplatform Dependencies without Dependency Hell\" src=\"modulator.png\"\u003e\n\n# modulator – `compileOnly` Kotlin Multiplatform Dependencies without Dependency Hell\n\n[![A-SIT Plus Official](https://raw.githubusercontent.com/a-sit-plus/a-sit-plus.github.io/709e802b3e00cb57916cbb254ca5e1a5756ad2a8/A-SIT%20Plus_%20official_opt.svg)](https://plus.a-sit.at/open-source.html)\n[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-brightgreen.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0)\n[![Gradle plugin portal](https://img.shields.io/gradle-plugin-portal/v/at.asitplus.gradle.modulator?label=Gradle%20plugin%20portal)](https://plugins.gradle.org/plugin/at.asitplus.gradle.modulator)\n\n\n**_modulator_ – a lean Gradle plugin that gives Kotlin Multiplatform the superpower of JVM-style `compileOnly` dependencies! No bloat, no dirty tricks, just clean, modular APIs, and full toolchain compatibility – forever!**\n\n\n\u003c/div\u003e\n\nImagine a Spring Boot core with optional persistence: JPA/Hibernate or MongoDB. You want expressive extension functions\nlike `Order.toJpaEntity()` or `Order.toMongoDocument()` to become available automatically when the corresponding starter\nis on the classpath, without forcing every service to depend on both stacks.  \nOn the JVM you’d create these adapters using `compileOnly` dependencies on JPA/Hibernate and MongoDB to keep your core clean.\nUnfortunately, _Kotlin Multiplatform says no!_  \nHence, optional, extension‑driven integrations either bloat dependency graphs or require tedious manual wiring.\n\n\nEnter _modulator_ – a lean Gradle plugin that brings two complementary capabilities to Kotlin Multiplatform:\n\n1. Piggyback modules with extension functionality and/or glue code on two or more `carrier` modules within a multi-module project.\n2. Automatically add those piggybacked modules as dependencies when all of their carriers are present in a consuming project.\n\n\nJust apply `at.asitplus.gradle.modulator` to any Gradle module that requires either capability.\nThat’s it – no custom wiring, no dependency clutter, no hacks, no compiler plugins, no code generation,\nbut full backwards compatibility with all Kotlin and Gradle tooling!\n\n\n## Quickstart\n_modulator_ introduces a new type of dependency: `carrier` dependencies, that are available alongside `api`, `implementation`, and so forth.\nA bridge / glue module depends on two or more carrier modules (within the same multi-module gradle project).\nWhen all carriers are present in a consumer, the bridge module is automatically pulled in.\n\n\n### Creating Bridge Modules (\"Modulation\")\nIf `bridgeModule` should provide glue functionality between `modA` and `modB`\n* apply the `at.asitplus.gradle.modulator` Gradle plugin\n* add `modA` and `modB` as `carrier` dependencies:\n\n```kotlin\n//build.gradle.kts of bridgeModule\n\nplugins {\n    alias(libs.plugins.kotlinMultiplatform)\n    `maven-publish`\n    /* …… */\n    id(\"at.asitplus.gradle.modulator\") version \"$modulatorVersion\"\n}\n\nkotlin {\n    listOf(\n        iosX64(),\n        iosArm64(),\n        iosSimulatorArm64()\n    ).forEach { iosTarget -\u003e\n        iosTarget.binaries.framework {\n            baseName = \"BridgeModule\"\n            isStatic = true\n        }\n    }\n\n    jvm()\n\n    // add additional targets as desired\n    \n    sourceSets {\n        //does not have to be commonMain, but it makes the most sense\n        commonMain.dependencies {\n           carrier(project(\":modA\")) //no need to modify modA's buildscript\n           carrier(project(\":modB\")) //no need to modify modB's buildscript\n        }\n    }\n}\n\n//…… publishing, etc.\n```\n\nThis will add metadata to both `modA` and `modB` publications, such that the published artifacts of both contain the information that\n`bridgeModule` should be pulled in when both `modA` and `modB` are added as dependencies to a consuming project.  \nThe buildscripts of neither `modA` nor `modB` require any changes or even the modulator gradle plugin.\n\n### Automagically Pulling in Bridge Modules (\"Demodulation\")\n\n_modulator_ works its magic in consuming projects even less obtrusively:\nJust apply the modulator Gradle plugin in consumers and the carrier dependencies as regular `api` or `implementation` dependencies.\nNo other changes are required to the buildscript.\n\n```kotlin\n//build.gradle.kts of consuming project\nplugins {\n    alias(libs.plugins.kotlinMultiplatform)\n    id(\"at.asitplus.gradle.modulator\") version \"$modulatorVersion\"\n}\n\nkotlin {\n    listOf(\n        iosX64(),\n        iosArm64(),\n        iosSimulatorArm64()\n    ).forEach { iosTarget -\u003e\n        iosTarget.binaries.framework {\n            baseName = \"ComposeApp\"\n            isStatic = true\n        }\n    }\n\n    jvm()\n    \n    // add additional targets as desired\n    \n    sourceSets {\n        commonMain.dependencies {\n            api(\"com.example.modA:$modAversion\")\n            api(\"com.example.modB:$modBversion\")\n            //that's it! bridgeModule will be automagically pulled in\n        }\n    }\n}\n```\n\nIf `modA` and `modB` are added as `api` dependencies, the bridge module will also be added as `api` dependency. The same holds\nfor `implementation` dependencies.\n\nFor library authors, this is not quite as hassle-free as `compileOnly` dependencies on the JVM but:\n* The project setup remains fully transparent, predictable, intelligible and easily maintainable.\n* The use of dedicated bridge modules and enriched Gradle metadata on carrier modules is fully and perfectly backwards-compatible with the whole Gradle/KMP ecosystem, and it will stay that way.\n* Your project either compiles or it does not run. No `RuntimeException` or other unpleasant surprises, because everything is known at compile-time.\n\nIn the end, no invasive changes to the KMP/Gradle tooling are required, as _modulator_ simply adds additional dependencies in the same way as adding them explicitly yourself.\n\n## Concrete Example Projects\nThe `example` directory contains two projects that showcase _modulator_:\n1. `modulatingProducer` contains three modules:\n   1. `cose` providing a single sample COSE-ish data class\n   2. `jose` providing a single sample JOSE-ish data class\n   3. `coseToJose` providing mapper functionality from COSE to JOSE\n2. `modulatedConsumer` contains a single module that adds `cose` and `jose` dependencies and uses the mapping functionality provided by `cosetoJose`, showcasing that no explicit adding of this dependency is needed\n\nTo try it out: publish `modulatingProduce` to maven local and open `modulatedConsumer` in IDEA to witness the magic!\n\n\n\u003chr\u003e\n\n\u003cp align=\"center\"\u003e\nThe Apache License does not apply to the logos, (including the A-SIT logo) and the project/module name(s), as these are the sole property of\nA-SIT/A-SIT Plus GmbH and may not be used in derivative works without explicit permission!\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa-sit-plus%2Fmodulator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fa-sit-plus%2Fmodulator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa-sit-plus%2Fmodulator/lists"}