CLOVIS
09/03/2025, 7:47 PMuseCommonJs() . To detect that, I'm using:
val moduleKind = project.objects.property<JsModuleKind>()
project.tasks.withType<org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrLink> {
moduleKind.set(compilerOptions.moduleKind)
}
but by the time moduleKind is set, it's not allowed to add a implementation(npm("foo", "1.0.0")) anymore.
I've also tried having a : RequiresNpmDependencies task with a dependency set that changes once moduleKind is known, and that works without the configuration cache, but with it I get a serialization crash within the configuration cache itself.Edoardo Luppi
09/03/2025, 8:05 PMEdoardo Luppi
09/03/2025, 8:06 PMVampire
09/03/2025, 8:13 PMimplementation(moduleKind.map { npm("foo", "1.0.0") })
?CLOVIS
09/04/2025, 7:56 AMCLOVIS
09/04/2025, 7:57 AMimplementation is resolved before moduleKind.set() is called…
If I could somehow do implementation(project.tasks.withType<…>().flatMap { it.compilerOptions.moduleKind }) then that may work maybe, but I don't see such a functionEdoardo Luppi
09/04/2025, 8:01 AMArtem Kobzar
09/04/2025, 8:48 AMVampire
09/04/2025, 9:16 AM@Vampire I tried, butMaybe you should find out why. Whenever order matters, usually something somewhere is doing something wrong. In this case probably doing dependency resolution at configuration time.is resolved beforeimplementationis called…moduleKind.set()
If I could somehow doThat would probably not help either, because you have the same problem. You already evilly break task-configuration avoidance by usingthen that may work maybe, but I don't see such a functionimplementation(project.tasks.withType<…>().flatMap { it.compilerOptions.moduleKind })
tasks.withType(...) { ... } which is the same as doing tasks.withType(...).all { ... }, and thus causes each and every task of that type to be configured, whether it will be executed or not and it does this immediately for all tasks that are already registered and for future ones as soon as they are registered.
So if with that above snippet and using the property like I suggested the property is "set too late", then there is not much you can do besides becoming even dirtier like using afterEvaluate { ... } , which is not good of course.
Maybe you should just not try to do that reaction?
If you want to proved the user convenience, maybe you should instead provide some function in your plugin that the user invokes and that then does both tasks for him, the one you want to react to and the reaction?
Or something like that.CLOVIS
09/04/2025, 9:21 AMkotlin.js().moduleKind should be a Provider in the Kotlin extension instead of only being a provider available on a task
• : RequiresNpmDependencies should be a ListProperty instead of a SetCLOVIS
09/04/2025, 9:27 AMmaybe you should instead provide some function in your plugin that the user invokes and that then does both tasks for himMaybe, but I don't think that approach scales. What if there was a third plugin that had to work with both KGP and my plugin? Should it provide its own config, if my plugin is there configure it, and otherwise configure KGP directly? Conceptually, this is the configuration of KGP. My plugin just needs an additional dependency to be able to deal with it, but it doesn't otherwise care. I could also not provide anything automatically at all and document users that they need to add the dependencies x and y for this to work, but IMO the point of Gradle is that you can trust the defaults.
Vampire
09/04/2025, 9:37 AMVampire
09/04/2025, 9:47 AMimplementation(
js()
.compilations
.named("main")
.flatMap { it.compileTaskProvider }
.flatMap { it.compilerOptions.moduleKind }
.filter { it == "MODULE_COMMONJS" }
.map { npm("foo", "1.0.0") }
)
maybe?Vampire
09/04/2025, 9:48 AMVampire
09/04/2025, 9:50 AMuseCommonJs() call is made, this should hopefully work properly.CLOVIS
09/04/2025, 5:57 PMval kotlinExt = project.kotlinExtension as KotlinMultiplatformExtension
val moduleKind = kotlinExt.js().compilations.named("main")
.flatMap { it.compileTaskProvider }
.flatMap { it.compilerOptions.moduleKind }
val plugins = moduleKind.map {
when (it) {
JsModuleKind.MODULE_COMMONJS, JsModuleKind.MODULE_UMD -> listOf(
ExternalVitePlugin("viteCommonjs", "vite-plugin-commonjs", "0.10.4"),
ExternalVitePlugin("commonjs", "@rollup/plugin-commonjs", "20.0.6"),
)
else -> emptyList()
}
}
project.tasks.withType(WriteConfig::class.java).configureEach {
config.plugins.addAll(plugins)
doFirst {
println("Plugins: ${plugins.get()}")
}
}
(this doesn't have the implementation part yet) but I get:
A problem was found with the configuration of task ':viteConfigureProd' (type 'WriteConfig').
- Type 'opensavvy.gradle.vite.base.tasks.WriteConfig' property 'plugins' doesn't have a configured value.
so I'm not sure it's going to workVampire
09/05/2025, 6:47 AMconfig.plugins.addAll, does the doFirst print the expected plugins?CLOVIS
09/05/2025, 7:43 AM* What went wrong:
Execution failed for task ':app:viteConfigureProd'.
> Cannot query the value of this provider because it has no value available.Vampire
09/05/2025, 9:01 AMmap it.
You probably need an orElse(emptyList()).Vampire
09/05/2025, 9:01 AMaddLater where an absent provider is just ignored.Vampire
09/05/2025, 9:02 AMplugins to config.plugins. I guess that is a ListProperty which becomes unset if you add an unset provider.CLOVIS
09/05/2025, 9:31 AMconfig.plugins is a ListProperty , but that one I own and there is already a .convention(emptyList())Vampire
09/05/2025, 10:26 AMaddAll an absent provider, that makes the whole property absent.
You need an orElse(emptyList()) on the provider you add to prevent that.Vampire
09/05/2025, 10:29 AMconfig.pluginsVampire
09/05/2025, 10:30 AMconvention is not used, because you have set an explicit value and that value is "absent"Vampire
09/05/2025, 10:30 AMnull which restores the convention vs. setting it to provider { null } which makes it absent.Adam Semenenko
09/15/2025, 10:05 AM@Artem Kobzar do you know how we do this inside KGP?afaik KGP doesn't conditionally add npm dependencies based on moduleKind. It just controls Webpack behaviour.
Adam Semenenko
09/15/2025, 10:08 AMThanks for the use-case, I've added it here https://youtrack.jetbrains.com/issue/KT-77145/KGP-JS-Migrate-configurable-DSL-to-use-Provider-API Did you find something that works?should be a Provider in the Kotlin extension instead of only being a provider available on a taskkotlin.js().moduleKind
Vampire
09/15/2025, 11:54 AM.orElse(emptyList()) should have done the trickCLOVIS
09/15/2025, 12:13 PM.orElse() can help here. The value is read before it is written, .orElse() will just give an empty list?Vampire
09/15/2025, 12:58 PMplugins ListProperty was absent because you added an absent provider derived from the moduleKind when no module kind was set.
I told you to use orElse(emptyList()) on the provider you add there so that it does not unset the whole ListProperty just because no moduleKind is set.