diff --git a/README.md b/README.md index bad24cb..f9042a9 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,9 @@ library meant to help reading and writing configuration files. - If you're looking for the Bloop codebase, look [here](https://github.com/scalacenter/bloop) - If you're looking to contribute, check our [CONTRIBUTING.md](./CONTRIBUTING.md) - If you're unfamiliar with Bloop and want to learn more, please check out our full [site](https://scalacenter.github.io/bloop/) + +## Binary compatibility + +bloop-config uses [unroll](https://github.com/com-lihaoyi/unroll/tree/main) to maintain binary compatibility. +If you add an option that breaks binary compatibility import scala.annotation.unroll and annotate the field with @unroll. +Give the field a reasonable default value. diff --git a/build.sc b/build.sc index 337a358..655de24 100644 --- a/build.sc +++ b/build.sc @@ -48,9 +48,11 @@ trait CommonPublish extends CiReleaseModule with Mima { trait Common extends CrossScalaModule with ScalafmtModule with ScalafixModule { val jsoniterVersion = "2.4.0" + val unrollVersion = "0.1.12" override def ivyDeps = Agg( - ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core::$jsoniterVersion" + ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core::$jsoniterVersion", + ivy"com.lihaoyi::unroll-annotation:$unrollVersion" ) override def compileIvyDeps = Agg( @@ -58,6 +60,11 @@ trait Common extends CrossScalaModule with ScalafmtModule with ScalafixModule { ) override def scalacOptions = Seq("-Ywarn-unused", "-deprecation") + + override def scalacPluginIvyDeps = T { + super.scalacPluginIvyDeps() ++ + Agg(ivy"com.lihaoyi::unroll-plugin:$unrollVersion") + } } trait CommonTest extends ScalaModule with TestModule.Munit { diff --git a/config/src/bloop/config/Config.scala b/config/src/bloop/config/Config.scala index e0b00cf..223ac0a 100644 --- a/config/src/bloop/config/Config.scala +++ b/config/src/bloop/config/Config.scala @@ -1,5 +1,7 @@ package bloop.config +import scala.annotation.unroll + import bloop.config.PlatformFiles.Path import bloop.config.PlatformFiles.emptyPath @@ -164,6 +166,15 @@ object Config { val All: List[String] = List(NoModule.id, CommonJSModule.id, ESModule.id) } + sealed abstract class ModuleSplitStyleJS(val id: String) + object ModuleSplitStyleJS { + case object FewestModules extends ModuleSplitStyleJS("FewestModules") + case object SmallestModules extends ModuleSplitStyleJS("SmallestModules") + case object SmallModulesFor extends ModuleSplitStyleJS("SmallModulesFor") + val All: List[String] = + List(FewestModules.id, SmallestModules.id, SmallModulesFor.id) + } + case class JsConfig( version: String, mode: LinkerMode, @@ -172,7 +183,8 @@ object Config { jsdom: Option[Boolean], output: Option[Path], nodePath: Option[Path], - toolchain: List[Path] + toolchain: List[Path], + @unroll moduleSplitStyle: Option[ModuleSplitStyleJS] = None ) extends PlatformConfig object JsConfig { diff --git a/config/src/bloop/config/ConfigCodecs.scala b/config/src/bloop/config/ConfigCodecs.scala index 586f4cc..719bbd4 100644 --- a/config/src/bloop/config/ConfigCodecs.scala +++ b/config/src/bloop/config/ConfigCodecs.scala @@ -131,6 +131,48 @@ object ConfigCodecs { } } + implicit val codecModuleSplitStyleJS + : JsonValueCodec[Config.ModuleSplitStyleJS] = { + new JsonValueCodec[Config.ModuleSplitStyleJS] { + val nullValue: Config.ModuleSplitStyleJS = + null.asInstanceOf[Config.ModuleSplitStyleJS] + def encodeValue(x: Config.ModuleSplitStyleJS, out: JsonWriter): Unit = { + val str = x match { + case Config.ModuleSplitStyleJS.FewestModules => + Config.ModuleSplitStyleJS.FewestModules.id + case Config.ModuleSplitStyleJS.SmallestModules => + Config.ModuleSplitStyleJS.SmallestModules.id + case Config.ModuleSplitStyleJS.SmallModulesFor => + Config.ModuleSplitStyleJS.SmallModulesFor.id + } + out.writeVal(str) + } + def decodeValue( + in: JsonReader, + default: Config.ModuleSplitStyleJS + ): Config.ModuleSplitStyleJS = + if (in.isNextToken('"')) { + in.rollbackToken() + in.readString(null) match { + case Config.ModuleSplitStyleJS.FewestModules.id => + Config.ModuleSplitStyleJS.FewestModules + case Config.ModuleSplitStyleJS.SmallestModules.id => + Config.ModuleSplitStyleJS.SmallestModules + case Config.ModuleSplitStyleJS.SmallModulesFor.id => + Config.ModuleSplitStyleJS.SmallModulesFor + case _ => + in.decodeError( + s"Expected module split style ${Config.ModuleSplitStyleJS.All + .mkString("'", "', '", "'")}" + ) + } + } else { + in.rollbackToken() + nullValue + } + } + } + implicit val codecJvmConfig: JsonValueCodec[Config.JvmConfig] = JsonCodecMaker.makeWithRequiredCollectionFields[Config.JvmConfig]