Repository: 7mind/izumi
Pull request #837 (2020-01-17 20:39:28)

Replace PluginSource with PluginConfig


Patch:
diff --git a/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/DebugProperties.scala b/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/DebugProperties.scala
index 8a697fa38..117cc47e2 100644
--- a/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/DebugProperties.scala
+++ b/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/DebugProperties.scala
@@ -3,7 +3,7 @@ package izumi.distage.plugins
 import izumi.fundamentals.platform.properties
 
 /**
-  * Java properties that control behavior of [[load.PluginLoader]]
+  * Java properties that control the behavior of [[load.PluginLoader]]
   *
   * @see [[DebugProperties]]
   */
diff --git a/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/PluginConfig.scala b/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/PluginConfig.scala
index c45e44c02..72004cd6e 100644
--- a/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/PluginConfig.scala
+++ b/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/PluginConfig.scala
@@ -1,15 +1,38 @@
 package izumi.distage.plugins
 
 final case class PluginConfig(
-                               packagesEnabled: Seq[String],
-                               packagesDisabled: Seq[String],
-                               cachePackages: Boolean,
-                               debug: Boolean,
-                             )
+                             packagesEnabled: Seq[String],
+                             packagesDisabled: Seq[String],
+                             cachePackages: Boolean,
+                             debug: Boolean,
+                             merges: Seq[PluginBase],
+                             overrides: Seq[PluginBase],
+                           ) {
+  def enablePackages(packagesEnabled: Seq[String]): PluginConfig = copy(packagesEnabled = this.packagesEnabled ++ packagesEnabled)
+  def enablePackage(packageEnabled: String): PluginConfig = enablePackages(Seq(packageEnabled))
+
+  def disablePackages(packagesDisabled: Seq[String]): PluginConfig = copy(packagesDisabled = this.packagesDisabled ++ packagesDisabled)
+  def disablePackage(packageDisabled: String): PluginConfig = disablePackages(Seq(packageDisabled))
+
+  def ++(plugins: Seq[PluginBase]): PluginConfig = copy(merges = merges ++ plugins)
+  def ++(plugin: PluginBase): PluginConfig = copy(merges = merges ++ Seq(plugin))
+
+  def overridenBy(plugins: Seq[PluginBase]): PluginConfig = copy(overrides = overrides ++ plugins)
+  def overridenBy(plugin: PluginBase): PluginConfig = copy(overrides = overrides ++ Seq(plugin))
+
+  def cachePackages(cachePackages: Boolean): PluginConfig = copy(cachePackages = cachePackages)
+  def debug(debug: Boolean): PluginConfig = copy(debug = debug)
+}
+
 object PluginConfig {
-  lazy val empty: PluginConfig = PluginConfig(Nil, Nil, cachePackages = false, debug = false)
-  def packages(packagesEnabled: Seq[String]): PluginConfig = PluginConfig(packagesEnabled, Nil, cachePackages = false, debug = false)
-  def cached(packagesEnabled: Seq[String]): PluginConfig = PluginConfig(packagesEnabled, Nil, cachePackages = cacheEnabled, debug = false)
+  /** Scan the specified packages for modules that inherit [[PluginBase]] */
+  def cached(packagesEnabled: Seq[String]): PluginConfig = PluginConfig(packagesEnabled, Nil, cachePackages = cacheEnabled, debug = false, Nil, Nil)
+  def packages(packagesEnabled: Seq[String]): PluginConfig = PluginConfig(packagesEnabled, Nil, cachePackages = false, debug = false, Nil, Nil)
+
+  /** Create a [[PluginConfig]] that simply returns the specified plugins */
+  def const(plugins: Seq[PluginBase]): PluginConfig = PluginConfig(Nil, Nil, cachePackages = false, debug = false, plugins, Nil)
+  def const(plugin: PluginBase): PluginConfig = const(Seq(plugin))
+  lazy val empty: PluginConfig = const(Nil)
 
   private[this] lazy val cacheEnabled: Boolean = {
     import izumi.fundamentals.platform.strings.IzString._
diff --git a/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/load/PluginLoader.scala b/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/load/PluginLoader.scala
index 0f13cf99a..607f6d217 100644
--- a/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/load/PluginLoader.scala
+++ b/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/load/PluginLoader.scala
@@ -3,16 +3,17 @@ package izumi.distage.plugins.load
 import izumi.distage.plugins.{PluginBase, PluginConfig}
 
 trait PluginLoader {
-  def load(): Seq[PluginBase]
+  def load(config: PluginConfig): Seq[PluginBase]
+
+  final def map(f: Seq[PluginBase] => Seq[PluginBase]): PluginLoader = c => f(load(c))
 }
 
 object PluginLoader {
   /** Create a [[PluginLoader]] that scans the classpath according to [[PluginConfig]] */
-  def apply(pluginConfig: PluginConfig): PluginLoader = new PluginLoaderDefaultImpl(pluginConfig)
-
-  /** Create a [[PluginLoader]] that returns the specified plugins */
-  def apply(plugins: Seq[PluginBase]): PluginLoader = () => plugins
-  def apply(plugins: PluginBase): PluginLoader = apply(Seq(plugins))
+  def apply(): PluginLoader = new PluginLoaderDefaultImpl()
 
-  lazy val empty: PluginLoader = () => Nil
+  /** Create a [[PluginLoader]] that ignores [[PluginConfig]] and returns the specified plugins */
+  def const(plugins: Seq[PluginBase]): PluginLoader = _ => plugins
+  def const(plugin: PluginBase): PluginLoader = _ => Seq(plugin)
+  lazy val empty: PluginLoader = const(Nil)
 }
diff --git a/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/load/PluginLoaderDefaultImpl.scala b/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/load/PluginLoaderDefaultImpl.scala
index 7f0a710ad..319a6e634 100644
--- a/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/load/PluginLoaderDefaultImpl.scala
+++ b/distage/distage-extension-plugins/src/main/scala/izumi/distage/plugins/load/PluginLoaderDefaultImpl.scala
@@ -7,15 +7,21 @@ import izumi.fundamentals.platform.cache.SyncCache
 
 import scala.jdk.CollectionConverters._
 
-class PluginLoaderDefaultImpl
-(
-  pluginConfig: PluginConfig,
-) extends PluginLoader {
-  def load(): Seq[PluginBase] = {
-    val config = pluginConfig.copy(packagesEnabled = pluginConfig.packagesEnabled)
+class PluginLoaderDefaultImpl extends PluginLoader {
+  override def load(config: PluginConfig): Seq[PluginBase] = {
+    /** Disable scanning if no packages are specified (enable `_root_` package if you really want to scan everything) */
+    val loadedPlugins = if (config.packagesEnabled.isEmpty && config.packagesDisabled.isEmpty) {
+      Seq.empty
+    } else {
+      scanClasspath(config)
+    }
+
+    applyOverrides(loadedPlugins, config)
+  }
 
-    val enabledPackages: Seq[String] = config.packagesEnabled.filterNot(config.packagesDisabled.contains)
-    val disabledPackages: Seq[String] = config.packagesDisabled
+  protected[this] def scanClasspath(config: PluginConfig): Seq[PluginBase] = {
+    val enabledPackages = config.packagesEnabled.filterNot(p => config.packagesDisabled.contains(p) || p == "_root_")
+    val disabledPackages = config.packagesDisabled
 
     val pluginBase = classOf[PluginBase]
     val pluginDef = classOf[PluginDef]
@@ -25,18 +31,25 @@ class PluginLoaderDefaultImpl
       PluginLoaderDefaultImpl.doLoad[PluginBase](pluginBase.getName, whitelistedClasses, pkgs, disabledPackages, config.debug)
     }
 
-    if (!pluginConfig.cachePackages) {
+    if (!config.cachePackages) {
       loadPkgs(enabledPackages)
     } else {
       val h1 = scala.util.hashing.MurmurHash3.seqHash(whitelistedClasses)
       val h2 = scala.util.hashing.MurmurHash3.seqHash(disabledPackages)
-      enabledPackages.flatMap{
-        p =>
-          val key = s"$p;$h1;$h2"
-          PluginLoaderDefaultImpl.cache.getOrCompute(key, loadPkgs(Seq(p)))
+      enabledPackages.flatMap {
+        pkg =>
+          val key = s"$pkg;$h1;$h2"
+          PluginLoaderDefaultImpl.cache.getOrCompute(key, loadPkgs(Seq(pkg)))
       }
     }
   }
+
+  protected[this] def applyOverrides(loadedPlugins: Seq[PluginBase], config: PluginConfig): Seq[PluginBase] = {
+    val merged = loadedPlugins ++ config.merges
+    if (config.overrides.nonEmpty) {
+      Seq((merged.merge +: config.overrides).overrideLeft)
+    } else merged
+  }
 }
 
 object PluginLoaderDefaultImpl {
diff --git a/distage/distage-framework/src/main/scala/izumi/distage/framework/model/AllLoadedPlugins.scala b/distage/distage-framework/src/main/scala/izumi/distage/framework/model/AllLoadedPlugins.scala
deleted file mode 100644
index 9d2ddff56..000000000
--- a/distage/distage-framework/src/main/scala/izumi/distage/framework/model/AllLoadedPlugins.scala
+++ /dev/null
@@ -1,18 +0,0 @@
-package izumi.distage.framework.model
-
-import izumi.distage.model.definition.{BootstrapModule, Module}
-import izumi.distage.plugins.PluginBase
-
-final case class AllLoadedPlugins(app: Seq[PluginBase], bootstrap: Seq[PluginBase] = Nil) {
-  def ++(that: AllLoadedPlugins): AllLoadedPlugins = AllLoadedPlugins(app ++ that.app, bootstrap ++ that.bootstrap)
-  def ++(appModule: Module): AllLoadedPlugins = ++(AllLoadedPlugins(Seq(appModule.morph[PluginBase]), Nil))
-  def ++(bootstrapModule: BootstrapModule): AllLoadedPlugins = ++(AllLoadedPlugins(Nil, Seq(bootstrapModule.morph[PluginBase])))
-
-  def overridenBy(that: AllLoadedPlugins): AllLoadedPlugins = AllLoadedPlugins(Seq(app.merge overridenBy that.app.merge), Seq(bootstrap.merge overridenBy that.bootstrap.merge))
-  def overridenBy(appModule: Module): AllLoadedPlugins = overridenBy(AllLoadedPlugins(Seq(appModule.morph[PluginBase]), Nil))
-  def overridenBy(bootstrapModule: BootstrapModule): AllLoadedPlugins = overridenBy(AllLoadedPlugins(Nil, Seq(bootstrapModule.morph[PluginBase])))
-}
-
-object AllLoadedPlugins {
-  lazy val empty: AllLoadedPlugins = AllLoadedPlugins(Nil, Nil)
-}
diff --git a/distage/distage-framework/src/main/scala/izumi/distage/framework/model/PluginSource.scala b/distage/distage-framework/src/main/scala/izumi/distage/framework/model/PluginSource.scala
deleted file mode 100644
index 826d454e6..000000000
--- a/distage/distage-framework/src/main/scala/izumi/distage/framework/model/PluginSource.scala
+++ /dev/null
@@ -1,65 +0,0 @@
-package izumi.distage.framework.model
-
-import izumi.distage.model.definition.{BootstrapModule, Module}
-import izumi.distage.plugins.load.PluginLoader
-import izumi.distage.plugins.{PluginBase, PluginConfig}
-
-sealed trait PluginSource {
-  def load(): AllLoadedPlugins
-
-  final def ++(that: PluginSource): PluginSource = PluginSource.Join(this, that)
-  final def map(f: AllLoadedPlugins => AllLoadedPlugins): PluginSource = PluginSource.Map(this, f)
-
-  final def ++(plugins: AllLoadedPlugins): PluginSource = map(_ ++ plugins)
-  final def ++(appModule: Module): PluginSource = map(_ ++ appModule)
-  final def ++(bootstrapModule: BootstrapModule): PluginSource = map(_ ++ bootstrapModule)
-
-  final def overridenBy(plugins: AllLoadedPlugins): PluginSource = map(_ overridenBy plugins)
-  final def overridenBy(appModule: Module): PluginSource = map(_ overridenBy appModule)
-  final def overridenBy(bootstrapModule: BootstrapModule): PluginSource = map(_ overridenBy bootstrapModule)
-}
-
-object PluginSource {
-  /** Load plugins from the specified packages */
-  def apply(pluginConfig: PluginConfig): PluginSource = Load(pluginConfig, PluginConfig.empty)
-  def apply(pluginConfig: PluginConfig, bootstrapPluginConfig: PluginConfig): PluginSource = Load(pluginConfig, bootstrapPluginConfig)
-
-  /** Load plugins using the specified loaders */
-  def apply(pluginLoader: PluginLoader): PluginSource = CustomLoad(pluginLoader, PluginLoader.empty)
-  def apply(pluginLoader: PluginLoader, bootstrapPluginLoader: PluginLoader): PluginSource = CustomLoad(pluginLoader, bootstrapPluginLoader)
-
-  /** Directly use the specified plugins */
-  def apply(plugins: Seq[PluginBase]): PluginSource = Const(AllLoadedPlugins(plugins))
-  def apply(plugins: Seq[PluginBase], bootstrapPlugins: Seq[PluginBase]): PluginSource = Const(AllLoadedPlugins(plugins, bootstrapPlugins))
-  def apply(allLoadedPlugins: AllLoadedPlugins): PluginSource = Const(allLoadedPlugins)
-
-  /** Empty */
-  lazy val empty: PluginSource = Const(AllLoadedPlugins.empty)
-
-  implicit final class PluginSourceMerge(private val pluginSources: Iterable[PluginSource]) extends AnyVal {
-    def merge: PluginSource = pluginSources.foldLeft(PluginSource.empty)(_ ++ _)
-  }
-
-  final case class Join(a: PluginSource, b: PluginSource) extends PluginSource {
-    override def load(): AllLoadedPlugins = a.load() ++ b.load()
-  }
-  final case class Map(a: PluginSource, f: AllLoadedPlugins => AllLoadedPlugins) extends PluginSource {
-    override def load(): AllLoadedPlugins = f(a.load())
-  }
-  final case class Load(pluginConfig: PluginConfig, bootstrapPluginConfig: PluginConfig) extends PluginSource {
-    override def load(): AllLoadedPlugins = {
-      val appPlugins = PluginLoader(pluginConfig).load()
-      val bsPlugins = PluginLoader(bootstrapPluginConfig).load()
-      AllLoadedPlugins(appPlugins, bsPlugins)
-    }
-  }
-
-  trait Custom extends PluginSource
-  final case class Const(plugins: AllLoadedPlugins) extends PluginSource.Custom {
-    override def load(): AllLoadedPlugins = plugins
-  }
-  final case class CustomLoad(pluginLoader: PluginLoader, bootstrapPluginLoader: PluginLoader) extends PluginSource {
-    override def load(): AllLoadedPlugins = AllLoadedPlugins(app = pluginLoader.load(), bootstrap = bootstrapPluginLoader.load())
-  }
-
-}
diff --git a/distage/distage-framework/src/main/scala/izumi/distage/framework/services/ConfigLoader.scala b/distage/distage-framework/src/main/scala/izumi/distage/framework/services/ConfigLoader.scala
index 1c4813e60..700366a6b 100644
--- a/distage/distage-framework/src/main/scala/izumi/distage/framework/services/ConfigLoader.scala
+++ b/distage/distage-framework/src/main/scala/izumi/distage/framework/services/ConfigLoader.scala
@@ -14,9 +14,7 @@ import scala.util.{Failure, Success, Try}
 trait ConfigLoader {
   def buildConfig(): AppConfig
 
-  final def map(f: AppConfig => AppConfig): ConfigLoader = {
-    () => f(buildConfig())
-  }
+  final def map(f: AppConfig => AppConfig): ConfigLoader = () => f(buildConfig())
 }
 
 object ConfigLoader {
diff --git a/distage/distage-framework/src/main/scala/izumi/distage/roles/RoleAppLauncherImpl.scala b/distage/distage-framework/src/main/scala/izumi/distage/roles/RoleAppLauncherImpl.scala
index e0c7b77a8..5ac3ef1b9 100644
--- a/distage/distage-framework/src/main/scala/izumi/distage/roles/RoleAppLauncherImpl.scala
+++ b/distage/distage-framework/src/main/scala/izumi/distage/roles/RoleAppLauncherImpl.scala
@@ -3,11 +3,13 @@ package izumi.distage.roles
 import distage._
 import izumi.distage.config.model.AppConfig
 import izumi.distage.framework.config.PlanningOptions
-import izumi.distage.framework.model.{ActivationInfo, AllLoadedPlugins, PluginSource}
+import izumi.distage.framework.model.ActivationInfo
 import izumi.distage.framework.services.ResourceRewriter.RewriteRules
 import izumi.distage.framework.services.{ConfigLoader, IntegrationChecker, ModuleProvider, RoleAppPlanner}
 import izumi.distage.model.definition.Activation
 import izumi.distage.model.effect.DIEffect
+import izumi.distage.plugins.{PluginBase, PluginConfig}
+import izumi.distage.plugins.load.PluginLoader
 import izumi.distage.plugins.merge.{PluginMergeStrategy, SimplePluginMergeStrategy}
 import izumi.distage.roles.RoleAppLauncher.Options
 import izumi.distage.roles.model.exceptions.DIAppBootstrapException
@@ -42,7 +44,8 @@ import scala.reflect.ClassTag
   * 16. Shutdown executors
   */
 abstract class RoleAppLauncherImpl[F[_]: TagK: DIEffect] extends RoleAppLauncher {
-  protected def pluginSource: PluginSource
+  protected def pluginConfig: PluginConfig
+  protected def bootstrapPluginConfig: PluginConfig = PluginConfig.empty
   protected def shutdownStrategy: AppShutdownStrategy[F]
 
   protected def referenceLibraryInfo: Seq[LibraryReference] = Vector.empty
@@ -57,45 +60,49 @@ abstract class RoleAppLauncherImpl[F[_]: TagK: DIEffect] extends RoleAppLauncher
     val earlyLogger = EarlyLoggers.makeEarlyLogger(parameters)
     showBanner(earlyLogger, referenceLibraryInfo)
 
-    val plugins = pluginSource.load()
-    val roles = loadRoles(parameters, earlyLogger, plugins)
+    val bsPlugins = makeBootstrapPluginLoader().load(bootstrapPluginConfig)
+    val appPlugins = makePluginLoader().load(pluginConfig)
+    val roles = loadRoles(parameters, earlyLogger, appPlugins, bsPlugins)
 
     // default PlanMergingPolicy will be applied to bootstrap module, so any non-trivial conflict in bootstrap bindings will fail the app
-    val defBs = makeBootstrapMergeStrategy(earlyLogger, parameters).merge(plugins.bootstrap)
+    val bsDefinition = makeBootstrapMergeStrategy(earlyLogger, parameters).merge(bsPlugins)
 
-    earlyLogger.info(s"Loaded ${defBs.bindings.size -> "bootstrap bindings"}...")
+    earlyLogger.info(s"Loaded ${bsDefinition.bindings.size -> "bootstrap bindings"}...")
 
     val config = makeConfigLoader(earlyLogger, parameters).buildConfig()
     val lateLogger = EarlyLoggers.makeLateLogger(parameters, earlyLogger, config)
 
     val mergeStrategy = makeMergeStrategy(lateLogger, parameters, roles)
-    val defApp = mergeStrategy.merge(plugins.app)
-    lateLogger.info(s"Loaded ${defApp.bindings.size -> "app bindings"}...")
+    val appDefinition = mergeStrategy.merge(appPlugins)
+    lateLogger.info(s"Loaded ${appDefinition.bindings.size -> "app bindings"}...")
 
-    validate(defBs, defApp)
+    validate(bsDefinition, appDefinition)
 
     val roots = gcRoots(roles)
 
-    val (activationInfo, activation) = new RoleAppActivationParser().parseActivation(earlyLogger, parameters, defApp, defaultActivations ++ requiredActivations)
+    val (activationInfo, activation) = new RoleAppActivationParser().parseActivation(earlyLogger, parameters, appDefinition, defaultActivations ++ requiredActivations)
 
     val options = contextOptions(parameters)
     val moduleProvider = makeModuleProvider(options, parameters, activationInfo, activation, roles, config, lateLogger)
-    val bsModule = moduleProvider.bootstrapModules().merge overridenBy defBs overridenBy bsOverride
+    val bsModule = moduleProvider.bootstrapModules().merge overridenBy bsDefinition overridenBy bsOverride
 
-    val appModule = moduleProvider.appModules().merge overridenBy defApp overridenBy appOverride
+    val appModule = moduleProvider.appModules().merge overridenBy appDefinition overridenBy appOverride
     val planner = makePlanner(options, bsModule, lateLogger)
     val appPlan = planner.makePlan(roots, appModule)
     lateLogger.info(s"Planning finished. ${appPlan.app.primary.keys.size -> "main ops"}, ${appPlan.app.side.keys.size -> "integration ops"}, ${appPlan.app.shared.keys.size -> "shared ops"}, ${appPlan.runtime.keys.size -> "runtime ops"}")
 
     val injector = appPlan.injector
-    val r = makeExecutor(parameters, roles, lateLogger, makeStartupExecutor(lateLogger, injector))
-    r.runPlan(appPlan)
+    val roleAppExecutor = makeExecutor(parameters, roles, lateLogger, makeStartupExecutor(lateLogger, injector))
+    roleAppExecutor.runPlan(appPlan)
   }
 
   protected def gcRoots(rolesInfo: RolesInfo): Set[DIKey] = rolesInfo.requiredComponents
   protected def makeBootstrapMergeStrategy(@unused lateLogger: IzLogger, @unused parameters: RawAppArgs): PluginMergeStrategy = SimplePluginMergeStrategy
   protected def makeMergeStrategy(@unused lateLogger: IzLogger, @unused parameters: RawAppArgs, @unused roles: RolesInfo): PluginMergeStrategy = SimplePluginMergeStrategy
 
+  protected def makePluginLoader(): PluginLoader = PluginLoader()
+  protected def makeBootstrapPluginLoader(): PluginLoader = PluginLoader()
+
   protected def makePlanner(options: PlanningOptions, bsModule: BootstrapModule, lateLogger: IzLogger): RoleAppPlanner[F] = {
     new RoleAppPlanner.Impl[F](options, bsModule, lateLogger)
   }
@@ -128,11 +135,11 @@ abstract class RoleAppLauncherImpl[F[_]: TagK: DIEffect] extends RoleAppLauncher
     )
   }
 
-  protected def loadRoles(parameters: RawAppArgs, logger: IzLogger, plugins: AllLoadedPlugins): RolesInfo = {
-    val bindings = plugins.app.flatMap(_.bindings)
-    val bsBindings = plugins.bootstrap.flatMap(_.bindings)
-    logger.info(s"Available ${plugins.app.size -> "app plugins"} with ${bindings.size -> "app bindings"} and ${
-      plugins.bootstrap.size -> "bootstrap plugins"} with ${bsBindings.size -> "bootstrap bindings"} ...")
+  protected def loadRoles(parameters: RawAppArgs, logger: IzLogger, appPlugins: Seq[PluginBase], bsPlugins: Seq[PluginBase]): RolesInfo = {
+    val bindings = appPlugins.flatMap(_.bindings)
+    val bsBindings = bsPlugins.flatMap(_.bindings)
+    logger.info(s"Available ${appPlugins.size -> "app plugins"} with ${bindings.size -> "app bindings"} and ${
+      bsPlugins.size -> "bootstrap plugins"} with ${bsBindings.size -> "bootstrap bindings"} ...")
 
     val activeRoleNames = parameters.roles.map(_.role).toSet
     val roleProvider = new RoleProvider.Impl(logger, activeRoleNames)
diff --git a/distage/distage-framework/src/main/scala/izumi/distage/staticinjector/plugins/macros/StaticPluginCheckerMacro.scala b/distage/distage-framework/src/main/scala/izumi/distage/staticinjector/plugins/macros/StaticPluginCheckerMacro.scala
index b110424e9..52e226902 100644
--- a/distage/distage-framework/src/main/scala/izumi/distage/staticinjector/plugins/macros/StaticPluginCheckerMacro.scala
+++ b/distage/distage-framework/src/main/scala/izumi/distage/staticinjector/plugins/macros/StaticPluginCheckerMacro.scala
@@ -7,9 +7,9 @@ import izumi.distage.bootstrap.BootstrapLocator
 import izumi.distage.config.AppConfigModule
 import izumi.distage.config.model.AppConfig
 import izumi.distage.model.PlannerInput
-import izumi.distage.plugins.{PluginBase, PluginConfig}
-import izumi.distage.plugins.load.PluginLoaderDefaultImpl
+import izumi.distage.plugins.load.PluginLoader
 import izumi.distage.plugins.merge.SimplePluginMergeStrategy
+import izumi.distage.plugins.{PluginBase, PluginConfig}
 import izumi.distage.roles.RoleAppLauncher.Options
 import izumi.distage.roles.services.RoleAppActivationParser
 import izumi.distage.staticinjector.plugins.ModuleRequirements
@@ -67,8 +67,7 @@ object StaticPluginCheckerMacro {
     val loadedPlugins = if (pluginPath == "") {
       Seq.empty
     } else {
-      val pluginLoader = new PluginLoaderDefaultImpl(PluginConfig.packages(Seq(pluginPath)))
-      pluginLoader.load()
+      PluginLoader().load(PluginConfig.packages(Seq(pluginPath)))
     }
 
     val configRegex = ReflectionUtil.getStringLiteral(c)(configFileRegex.tree)
diff --git a/distage/distage-framework/src/main/scala/izumi/distage/framework/model/BootstrapConfig.scala b/distage/distage-testkit-legacy/src/main/scala/izumi/distage/testkit/services/scalatest/adapter/BootstrapConfig.scala
similarity index 50%
rename from distage/distage-framework/src/main/scala/izumi/distage/framework/model/BootstrapConfig.scala
rename to distage/distage-testkit-legacy/src/main/scala/izumi/distage/testkit/services/scalatest/adapter/BootstrapConfig.scala
index 723e72647..5355571fe 100644
--- a/distage/distage-framework/src/main/scala/izumi/distage/framework/model/BootstrapConfig.scala
+++ b/distage/distage-testkit-legacy/src/main/scala/izumi/distage/testkit/services/scalatest/adapter/BootstrapConfig.scala
@@ -1,11 +1,9 @@
-package izumi.distage.framework.model
+package izumi.distage.testkit.services.scalatest.adapter
 
-import distage.plugins.PluginConfig
+import izumi.distage.plugins.PluginConfig
 
+@deprecated("Use dstest", "2019/Jul/18")
 final case class BootstrapConfig(
                                   pluginConfig: PluginConfig,
-                                  bootstrapPluginConfig: Option[PluginConfig] = None,
+                                  bootstrapPluginConfig: Option[PluginConfig],
                                 )
-object BootstrapConfig {
-  def empty: BootstrapConfig = BootstrapConfig(PluginConfig.empty, None)
-}
diff --git a/fundamentals/fundamentals-platform/.jvm/src/main/scala/izumi/fundamentals/platform/jvm/IzJvm.scala b/fundamentals/fundamentals-platform/.jvm/src/main/scala/izumi/fundamentals/platform/jvm/IzJvm.scala
index 7543c40e2..8facb30e9 100644
--- a/fundamentals/fundamentals-platform/.jvm/src/main/scala/izumi/fundamentals/platform/jvm/IzJvm.scala
+++ b/fundamentals/fundamentals-platform/.jvm/src/main/scala/izumi/fundamentals/platform/jvm/IzJvm.scala
@@ -2,6 +2,7 @@ package izumi.fundamentals.platform.jvm
 
 import java.lang.management.ManagementFactory
 import java.net.{URLClassLoader, URLDecoder}
+import java.nio.file.{Path, Paths}
 import java.time.ZonedDateTime
 
 import scala.annotation.tailrec
@@ -22,6 +23,8 @@ trait IzJvm {
     maybeTerm.isDefined
   }
 
+  def tempDir: Path = Paths.get(System.getProperty("java.io.tmpdir"))
+
   def terminalColorsEnabled: Boolean = {
     import izumi.fundamentals.platform.basics.IzBoolean._
 


Test patch:
diff --git a/distage/distage-framework/src/test/scala/izumi/distage/roles/test/RoleAppTest.scala b/distage/distage-framework/src/test/scala/izumi/distage/roles/test/RoleAppTest.scala
index 6e2566d72..0487dc3af 100644
--- a/distage/distage-framework/src/test/scala/izumi/distage/roles/test/RoleAppTest.scala
+++ b/distage/distage-framework/src/test/scala/izumi/distage/roles/test/RoleAppTest.scala
@@ -9,12 +9,12 @@ import distage.plugins.{PluginBase, PluginDef}
 import distage.{DIKey, Injector, Locator}
 import izumi.distage.effect.modules.IdentityDIEffectModule
 import izumi.distage.framework.config.PlanningOptions
-import izumi.distage.framework.model.PluginSource
 import izumi.distage.framework.services.{IntegrationChecker, RoleAppPlanner}
 import izumi.distage.model.Locator.LocatorRef
 import izumi.distage.model.definition.{BootstrapModule, DIResource}
+import izumi.distage.plugins.PluginConfig
 import izumi.distage.roles.RoleAppMain
-import izumi.distage.roles.test.fixtures.Fixture.{TestResource, IntegrationResource0, IntegrationResource1, XXX_ResourceEffectsRecorder}
+import izumi.distage.roles.test.fixtures.Fixture.{IntegrationResource0, IntegrationResource1, TestResource, XXX_ResourceEffectsRecorder}
 import izumi.distage.roles.test.fixtures._
 import izumi.distage.roles.test.fixtures.roles.TestRole00
 import izumi.fundamentals.platform.functional.Identity
@@ -56,7 +56,7 @@ class RoleAppTest extends AnyWordSpec
 
       new RoleAppMain.Silent(
         new TestLauncher {
-          override protected def pluginSource: PluginSource = super.pluginSource overridenBy probe
+          override protected def pluginConfig: PluginConfig = super.pluginConfig overridenBy probe
         }
       ).main(Array(
         "-ll", logLevel,
@@ -73,8 +73,8 @@ class RoleAppTest extends AnyWordSpec
 
       new RoleAppMain.Silent(
         new TestLauncher {
-          override protected def pluginSource: PluginSource = {
-            PluginSource(Seq(
+          override protected def pluginConfig: PluginConfig = {
+            PluginConfig.const(Seq(
               new ResourcesPluginBase().morph[PluginBase],
               new ConflictPlugin,
               new TestPlugin,
diff --git a/distage/distage-framework/src/test/scala/izumi/distage/roles/test/TestLauncher.scala b/distage/distage-framework/src/test/scala/izumi/distage/roles/test/TestLauncher.scala
index 3e8c1265b..1bd4170cf 100644
--- a/distage/distage-framework/src/test/scala/izumi/distage/roles/test/TestLauncher.scala
+++ b/distage/distage-framework/src/test/scala/izumi/distage/roles/test/TestLauncher.scala
@@ -1,15 +1,13 @@
 package izumi.distage.roles.test
 
 import cats.effect.IO
-import izumi.distage.framework.model.PluginSource
 import izumi.distage.plugins.PluginConfig
 import izumi.distage.roles.AppShutdownStrategy.ImmediateExitShutdownStrategy
 import izumi.distage.roles._
 import izumi.fundamentals.platform.language.SourcePackageMaterializer.thisPkg
 
 class TestLauncher extends RoleAppLauncher.LauncherF[IO] {
-  override protected def pluginSource: PluginSource = PluginSource(
+  override protected def pluginConfig: PluginConfig =
     PluginConfig.cached(Seq(s"$thisPkg.fixtures"))
-  )
   override protected val shutdownStrategy: AppShutdownStrategy[IO] = new ImmediateExitShutdownStrategy()
 }
diff --git a/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/TestConfig.scala b/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/TestConfig.scala
index 9536dfd5a..f8c69fb99 100644
--- a/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/TestConfig.scala
+++ b/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/TestConfig.scala
@@ -1,27 +1,29 @@
 package izumi.distage.testkit
 
 import distage.{Activation, BootstrapModule, DIKey, Module, StandardAxis}
-import izumi.distage.framework.model.PluginSource
 import izumi.distage.plugins.PluginConfig
 
 /**
-  * @param pluginSource       Source of module definitions from which to build object graphs for each tests.
-  *                           Each [[PluginSource]] creates a distinct memoization group (aka [[izumi.distage.testkit.services.dstest.TestEnvironment]]).
-  *                           objects will be memoized only between tests in the same memoization group
+  * @param pluginConfig          Source of module definitions from which to build object graphs for each tests.
+  *                              Each [[PluginConfig]] creates a distinct memoization group (aka [[izumi.distage.testkit.services.dstest.TestEnvironment]]).
+  *                              objects will be memoized only between tests in the same memoization group
   *
-  * @param activation         Chosen configurations. Different [[Activation]]s will create distinct memoization groups
+  * @param bootstrapPluginConfig Same as [[pluginConfig]], but for [[BootstrapModule]]
   *
-  * @param memoizationRoots   Every distinct set of `memoizationRoots` will create a distinct memoization group
-  *                           for tests with the exact same `memoizationRoots`
+  * @param activation            Chosen configurations. Different [[Activation]]s will create distinct memoization groups
   *
-  * @param moduleOverrides    Override loaded plugins with a given [[Module]]. Using overrides
-  *                           will create a distinct memoization group, i.e. objects will be
-  *                           memoized only between tests with the exact same overrides
+  * @param memoizationRoots      Every distinct set of `memoizationRoots` will create a distinct memoization group
+  *                              for tests with the exact same `memoizationRoots`
   *
-  * @param bootstrapOverrides Same as [[moduleOverrides]], but for [[BootstrapModule]]
+  * @param moduleOverrides       Override loaded plugins with a given [[Module]]. Using overrides
+  *                              will create a distinct memoization group, i.e. objects will be
+  *                              memoized only between tests with the exact same overrides
+  *
+  * @param bootstrapOverrides    Same as [[moduleOverrides]], but for [[BootstrapModule]]
   */
 final case class TestConfig(
-                             pluginSource: PluginSource,
+                             pluginConfig: PluginConfig,
+                             bootstrapPluginConfig: PluginConfig = PluginConfig.empty,
                              activation: Activation = StandardAxis.testProdActivation,
                              memoizationRoots: Set[DIKey] = Set.empty,
                              moduleOverrides: Module = Module.empty,
@@ -29,5 +31,5 @@ final case class TestConfig(
                            )
 
 object TestConfig {
-  def forSuite(clazz: Class[_]): TestConfig = TestConfig(PluginSource(PluginConfig.cached(Seq(clazz.getPackage.getName))))
+  def forSuite(clazz: Class[_]): TestConfig = TestConfig(PluginConfig.cached(Seq(clazz.getPackage.getName)))
 }
diff --git a/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/services/dstest/DistageTestEnv.scala b/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/services/dstest/DistageTestEnv.scala
index 9b9a909d3..cd23f0d70 100644
--- a/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/services/dstest/DistageTestEnv.scala
+++ b/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/services/dstest/DistageTestEnv.scala
@@ -1,5 +1,6 @@
 package izumi.distage.testkit.services.dstest
 
+import distage.plugins.PluginLoader
 import izumi.distage.framework.activation.PruningPlanMergingPolicyLoggedImpl
 import izumi.distage.framework.model.ActivationInfo
 import izumi.distage.framework.services.ActivationInfoExtractor
@@ -13,13 +14,13 @@ import izumi.fundamentals.platform.language.unused
 import izumi.logstage.api.IzLogger
 
 trait DistageTestEnv {
-  protected[distage] def loadEnvironment(logger: IzLogger, testConfig: TestConfig): TestEnvironment = {
+  protected[distage] def loadEnvironment(logger: IzLogger, testConfig: TestConfig, pluginLoader: PluginLoader): TestEnvironment = {
     val roles = loadRoles(logger)
     val mergeStrategy = makeMergeStrategy(logger)
 
     import izumi.fundamentals.platform.strings.IzString._
     def doMake(): TestEnvironment = {
-      makeEnv(logger, testConfig, roles, mergeStrategy)
+      makeEnv(logger, testConfig, pluginLoader, roles, mergeStrategy)
     }
 
     if (DebugProperties.`izumi.distage.testkit.environment.cache`.asBoolean(true)) {
@@ -29,10 +30,11 @@ trait DistageTestEnv {
     }
   }
 
-  private def makeEnv(logger: IzLogger, testConfig: TestConfig, roles: RolesInfo, mergeStrategy: PluginMergeStrategy): TestEnvironment = {
-    val plugins = testConfig.pluginSource.load()
-    val appModule = mergeStrategy.merge(plugins.app) overridenBy testConfig.moduleOverrides
-    val bootstrapModule = mergeStrategy.merge(plugins.bootstrap) overridenBy testConfig.bootstrapOverrides
+  private def makeEnv(logger: IzLogger, testConfig: TestConfig, pluginLoader: PluginLoader, roles: RolesInfo, mergeStrategy: PluginMergeStrategy): TestEnvironment = {
+    val appPlugins = pluginLoader.load(testConfig.pluginConfig)
+    val bsPlugins = pluginLoader.load(testConfig.bootstrapPluginConfig)
+    val appModule = mergeStrategy.merge(appPlugins) overridenBy testConfig.moduleOverrides
+    val bootstrapModule = mergeStrategy.merge(bsPlugins) overridenBy testConfig.bootstrapOverrides
     val availableActivations = ActivationInfoExtractor.findAvailableChoices(logger, appModule)
     val activation = testConfig.activation
 
@@ -64,7 +66,6 @@ trait DistageTestEnv {
 }
 
 object DistageTestEnv {
-
   private lazy val cache = new SyncCache[EnvCacheKey, TestEnvironment]
 
   case class EnvCacheKey(config: TestConfig, rolesInfo: RolesInfo, mergeStrategy: PluginMergeStrategy)
diff --git a/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/services/dstest/DistageTestRunner.scala b/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/services/dstest/DistageTestRunner.scala
index 6be521e38..0c295aabd 100644
--- a/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/services/dstest/DistageTestRunner.scala
+++ b/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/services/dstest/DistageTestRunner.scala
@@ -43,7 +43,8 @@ class DistageTestRunner[F[_] : TagK]
     val config = loader.buildConfig()
     val checker = new PlanCircularDependencyCheck(options, logger)
 
-    logger.info(s"Starting tests across ${groups.size -> "num envs"}")
+    import izumi.fundamentals.platform.strings.IzString._
+    logger.info(s"Starting tests across ${groups.size -> "num envs"} ${TagK[F].tag} ${groups.map(_._2.map(_.meta.id)).niceList()}")
     logger.trace(s"Env contents: ${groups.keys -> "test environments"}")
 
     groups.foreach {
diff --git a/distage/distage-testkit-legacy/src/main/scala/izumi/distage/testkit/services/scalatest/adapter/DistagePluginTestSupport.scala b/distage/distage-testkit-legacy/src/main/scala/izumi/distage/testkit/services/scalatest/adapter/DistagePluginTestSupport.scala
index c5bea456b..33a0db4b2 100644
--- a/distage/distage-testkit-legacy/src/main/scala/izumi/distage/testkit/services/scalatest/adapter/DistagePluginTestSupport.scala
+++ b/distage/distage-testkit-legacy/src/main/scala/izumi/distage/testkit/services/scalatest/adapter/DistagePluginTestSupport.scala
@@ -1,17 +1,16 @@
 package izumi.distage.testkit.services.scalatest.adapter
 
 import distage.SafeType
-import izumi.distage.framework.model.{BootstrapConfig, PluginSource}
 import izumi.distage.model.definition.Activation
 import izumi.distage.model.definition.StandardAxis._
 import izumi.distage.plugins.PluginConfig
+import izumi.distage.plugins.load.PluginLoader
 import izumi.distage.testkit.TestConfig
 import izumi.distage.testkit.services.dstest.{DistageTestEnv, TestEnvironment}
+import izumi.fundamentals.platform.language.unused
 import izumi.fundamentals.reflection.Tags.TagK
 import izumi.logstage.api.IzLogger
 
-
-
 @deprecated("Use dstest", "2019/Jul/18")
 abstract class DistagePluginTestSupport[F[_] : TagK] extends DistageTestSupport[F] with DistageTestEnv {
 
@@ -30,9 +29,8 @@ abstract class DistagePluginTestSupport[F[_] : TagK] extends DistageTestSupport[
   protected def pluginBootstrapPackages: Option[Seq[String]] = None
 
   override final def loadEnvironment(logger: IzLogger): TestEnvironment = {
-    val pluginSource = makePluginLoader(bootstrapConfig)
-    val config = TestConfig(pluginSource, activation)
-    loadEnvironment(logger, config)
+    val config = TestConfig(bootstrapConfig.pluginConfig, bootstrapConfig.bootstrapPluginConfig.getOrElse(PluginConfig.empty), activation)
+    loadEnvironment(logger, config, makePluginLoader(bootstrapConfig))
   }
 
   protected def activation: Activation = {
@@ -45,14 +43,12 @@ abstract class DistagePluginTestSupport[F[_] : TagK] extends DistageTestSupport[
 
   protected def bootstrapConfig: BootstrapConfig = {
     BootstrapConfig(
-      PluginConfig.cached(pluginPackages),
-      pluginBootstrapPackages.map(p => PluginConfig.cached(p)),
+      pluginConfig = PluginConfig.cached(pluginPackages),
+      bootstrapPluginConfig = pluginBootstrapPackages.map(p => PluginConfig.cached(p)),
     )
   }
 
-  protected def makePluginLoader(bootstrapConfig: BootstrapConfig): PluginSource = {
-    PluginSource(bootstrapConfig.pluginConfig, bootstrapConfig.bootstrapPluginConfig.getOrElse(PluginConfig.empty))
-  }
+  protected def makePluginLoader(@unused bootstrapConfig: BootstrapConfig): PluginLoader = PluginLoader()
 
 }
 
diff --git a/distage/distage-testkit-legacy/src/main/scala/izumi/distage/testkit/services/scalatest/adapter/MemoizationContextId.scala b/distage/distage-testkit-legacy/src/main/scala/izumi/distage/testkit/services/scalatest/adapter/MemoizationContextId.scala
index 625137c6c..84a27f4cc 100644
--- a/distage/distage-testkit-legacy/src/main/scala/izumi/distage/testkit/services/scalatest/adapter/MemoizationContextId.scala
+++ b/distage/distage-testkit-legacy/src/main/scala/izumi/distage/testkit/services/scalatest/adapter/MemoizationContextId.scala
@@ -1,7 +1,6 @@
 package izumi.distage.testkit.services.scalatest.adapter
 
 import distage.{Activation, SafeType}
-import izumi.distage.framework.model.BootstrapConfig
 
 @deprecated("Use dstest", "2019/Jul/18")
 sealed trait MemoizationContextId
diff --git a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/DistageAbstractScalatestSpec.scala b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/DistageAbstractScalatestSpec.scala
index e3f915d33..934db25c8 100644
--- a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/DistageAbstractScalatestSpec.scala
+++ b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/DistageAbstractScalatestSpec.scala
@@ -3,6 +3,7 @@ package izumi.distage.testkit.services.scalatest.dstest
 import distage.{Tag, TagK, TagKK}
 import izumi.distage.model.effect.DIEffect
 import izumi.distage.model.providers.ProviderMagnet
+import izumi.distage.plugins.load.PluginLoader
 import izumi.distage.testkit.TestConfig
 import izumi.distage.testkit.services.dstest.DistageTestRunner.{DistageTest, TestId, TestMeta}
 import izumi.distage.testkit.services.dstest._
@@ -33,7 +34,7 @@ trait DistageAbstractScalatestSpec[F[_]]
 
   final protected lazy val testEnv: TestEnvironment = makeTestEnv()
 
-  protected def makeTestEnv(): TestEnvironment = loadEnvironment(logger, config)
+  protected def makeTestEnv(): TestEnvironment = loadEnvironment(logger, config, PluginLoader())
 
   protected def distageSuiteName: String = getSimpleNameOfAnObjectsClass(this)
   protected def distageSuiteId: String = this.getClass.getName
diff --git a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/DistageTestExample.scala b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/DistageTestExample.scala
index 5790e2d3b..208cf5dc9 100644
--- a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/DistageTestExample.scala
+++ b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/DistageTestExample.scala
@@ -4,10 +4,9 @@ import java.util.concurrent.atomic.{AtomicInteger, AtomicReference}
 
 import cats.effect.{IO => CIO}
 import distage._
-import izumi.distage.framework.model.PluginSource
+import distage.plugins.PluginDef
 import izumi.distage.model.effect.DIEffect
 import izumi.distage.model.effect.DIEffect.syntax._
-import izumi.distage.plugins.PluginConfig
 import izumi.distage.testkit.TestConfig
 import izumi.distage.testkit.distagesuite.DistageTestExampleBase._
 import izumi.distage.testkit.distagesuite.fixtures.{ApplePaymentProvider, MockCache, MockCachedUserService, MockUserRepository}
@@ -15,6 +14,7 @@ import izumi.distage.testkit.scalatest.{DistageBIOSpecScalatest, DistageSpecScal
 import izumi.distage.testkit.services.scalatest.dstest.DistageAbstractScalatestSpec
 import izumi.fundamentals.platform.functional.Identity
 import izumi.fundamentals.platform.language.Quirks
+import izumi.fundamentals.platform.language.Quirks._
 import org.scalatest.exceptions.TestFailedException
 import zio.Task
 
@@ -44,30 +44,30 @@ class DistageTestExampleBIO extends DistageBIOSpecScalatest[zio.IO] with Distage
 
 object DistageTestExampleBase {
   class SetCounter {
-    import Quirks._
     private[this] val c: AtomicInteger = new AtomicInteger(0)
     def inc(): Unit = c.incrementAndGet().discard()
     def get: Int = c.get()
   }
   sealed trait SetElement {
-    Quirks.discard(counter.inc())
+    locally {
+      counter.inc()
+    }
     def counter: SetCounter
-    def v: Int
   }
-  final case class SetElement1(v: Int, counter: SetCounter) extends SetElement
-  final case class SetElement2(v: Int, counter: SetCounter) extends SetElement
-  final case class SetElement3(v: Int, counter: SetCounter) extends SetElement
+  final case class SetElement1(counter: SetCounter) extends SetElement
+  final case class SetElement2(counter: SetCounter) extends SetElement
+  final case class SetElement3(counter: SetCounter) extends SetElement
 }
 
-abstract class DistageTestExampleBase[F[_] : TagK](implicit F: DIEffect[F]) extends DistageSpecScalatest[F] with DistageMemoizeExample[F] {
+abstract class DistageTestExampleBase[F[_]: TagK](implicit F: DIEffect[F]) extends DistageSpecScalatest[F] with DistageMemoizeExample[F] {
 
   override protected def config: TestConfig = super.config.copy(
-    pluginSource = super.config.pluginSource ++ PluginSource(PluginConfig.cached(Seq("xxx"))) ++ new ModuleDef {
+    pluginConfig = super.config.pluginConfig.enablePackage("xxx") ++ new PluginDef {
       make[SetCounter]
 
-      make[SetElement1].from(SetElement1(1, _))
-      make[SetElement2].from(SetElement2(2, _))
-      make[SetElement3].from(SetElement3(3, _))
+      make[SetElement1]
+      make[SetElement2]
+      make[SetElement3]
 
       many[SetElement]
         .weak[SetElement1]
@@ -97,7 +97,7 @@ abstract class DistageTestExampleBase[F[_] : TagK](implicit F: DIEffect[F]) exte
 
     "support memoized named weak sets" in {
       (
-        set: Set[SetElement]@Id("set-id"),
+        set: Set[SetElement] @Id("set-id"),
         s1: SetElement1,
         s2: SetElement2,
         s3: SetElement3,