Intercepting my plugin's configuration

Hello everybody,
I’m here to ask for suggestions.

I’m designing and implementing a Gradle plugin, say FooPlugin, in Kotlin.
The plugin relies on a FooExtension which is created and added to a project upon plugin application:

class FooPlugin : Plugin<Project> {
    override fun apply(target: Project) {
         val extension = target.extensions.create("foo", FooExtension::class.java)
         // set up default values for extension properties
    }
}

If I well understood how plugin development works, the notation above implies that, whenever someone imports my plugin in their build.gradle.kts, the default values of FooExtension properties are computed upon FooPlugin application.

Yet, users can write:

foo {
   // override FooExtension properties values here
}

to override my default values.

Now suppose my default values are not available at plugin application time because, for instance, they can only be computed after some other plugin has been computed.

This is, for instance, the case of the org.jetbrains.kotlin.js plugin, aimed at setting up Kotlin JS projects.
In fact, it requires the user to choose among a Node- or Browser-compliant compilation fashion (or both), by means of the following syntax:

kotlin  {
    js {
         nodeJs{ } // or browser { }
    }
}

There, the nodeJs{ } creates new tasks for handling NodeJS projects.

My problem is that I rely on the existence of these tasks to set up FooExtension properties’ default values.
So, currently, I endowed my FooExtension with a method

fun defaultValuesFrom(project: Project) { /* updates the properties of FooExtension by inspecting the tasks currently in project */ }

which is capable of inspecting the tasks currently hosted by a project to set up the FooExtension.

This solution actually works, but it requires users to use my plugin as follows:

kotlin  {
    js {
         nodeJs{ } // or browser { }
    }
}

val project = this

// foo MUST be configured after kotlin
foo {
   defaultValuesFrom(project)  // this must be called EXPLICITLY by users
}

In particular, it bothers me that users must explicitly call defaultValuesFrom(project) .
I’d rather invoke it automatically behind the scenes, upon invocation of foo, i.e. upon my plugin configuration.

However, I found no way to intercept or override my plugin configuration to inject my custom action.

My questions are:

  • is there any way to do so?
  • is there a better design for my needs?

Firstly, if your plugin requires another plugin to be applied, why not apply the org.jetbrains.kotlin.js plugin as the first thing your plugin does? Then you can be guaranteed that the jetbrains extension is available before your logic executes. Eg

class FooPlugin implements Plugin<Project> {
   void apply(Project project) {
      project.plugins.apply("org.jetbrains.kotlin.js")
      // extra logic here 
   } 
} 

Secondly, you could use lazy configuration to separate the value of a property from the source of the value. Eg: declare your task properties as a Provider and wire them to the jetbrains js extension properties before the actual value is set. And the value lookup will happen “just in time”.

Lastly, if a property is a collection its likely the jetbrains team have used a subclass of DomainObjectCollection. If so there are “live” methods (eg matching(Closure) and all(Closure)) where you can get events for previous and future additions to the collection.