diff --git a/core/src/main/scala/com/lightbend/paradox/markdown/Directive.scala b/core/src/main/scala/com/lightbend/paradox/markdown/Directive.scala index 96f03c69..c7a6602d 100644 --- a/core/src/main/scala/com/lightbend/paradox/markdown/Directive.scala +++ b/core/src/main/scala/com/lightbend/paradox/markdown/Directive.scala @@ -695,6 +695,9 @@ case class InlineGroupDirective(groups: Seq[String]) extends InlineDirective(gro * Dependency directive. */ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective("dependency") { + val VersionSymbol = "symbol" + val VersionValue = "value" + val variables = ctx.properties val ScalaVersion = variables.get("scala.version") val ScalaBinaryVersion = variables.get("scala.binary.version") @@ -709,6 +712,9 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective(" def renderDependency(tools: String, node: DirectiveNode, printer: Printer): Unit = { val classes = Seq("dependency", node.attributes.classesString).filter(_.nonEmpty) + val symbolPostfixes = node.attributes.keys().asScala.toSeq + .filter(_.startsWith(VersionSymbol)).sorted.map(_.replace(VersionSymbol, "")) + val dependencyPostfixes = node.attributes.keys().asScala.toSeq .filter(_.startsWith("group")).sorted.map(_.replace("group", "")) @@ -729,6 +735,10 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective(" "" } + val symbols = symbolPostfixes.map { sp => + requiredCoordinate(VersionSymbol + sp) + }.toSet + def sbt(group: String, artifact: String, version: String, scope: Option[String], classifier: Option[String]): String = { val scopeString = scope.map { case s @ ("runtime" | "compile" | "test") => " % " + s.capitalize @@ -736,29 +746,31 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective(" } val classifierString = classifier.map(" classifier " + '"' + _ + '"') val extra = (scopeString ++ classifierString).mkString + val versionOrSymbol = if (symbols.contains(version)) version else s""""$version"""" (ScalaVersion, ScalaBinaryVersion) match { case (Some(scalaVersion), _) if artifact.endsWith("_" + scalaVersion) => val strippedArtifact = artifact.substring(0, artifact.length - 1 - scalaVersion.length) - s""""$group" % "$strippedArtifact" % "$version"$extra cross CrossVersion.full""" + s""""$group" % "$strippedArtifact" % ${versionOrSymbol}$extra cross CrossVersion.full""" case (_, Some(scalaBinVersion)) if artifact.endsWith("_" + scalaBinVersion) => val strippedArtifact = artifact.substring(0, artifact.length - 1 - scalaBinVersion.length) - s""""$group" %% "$strippedArtifact" % "$version"$extra""" + s""""$group" %% "$strippedArtifact" % ${versionOrSymbol}$extra""" case _ => - s""""$group" % "$artifact" % "$version"$extra""" + s""""$group" % "$artifact" % $versionOrSymbol$extra""" } } def gradle(group: String, artifact: String, version: String, scope: Option[String], classifier: Option[String]): String = { val conf = scope.getOrElse("compile") val extra = classifier.map(c => s", classifier: '$c'").getOrElse("") - s"""$conf group: '$group', name: '$artifact', version: '$version'$extra""".stripMargin + val v = if (symbols.contains(version)) s"versions.$version" else s"'$version'" + s"""$conf group: '$group', name: '$artifact', version: $v$extra""".stripMargin } def mvn(group: String, artifact: String, version: String, scope: Option[String], classifier: Option[String]): String = { val elements = - Seq("groupId" -> group, "artifactId" -> artifact, "version" -> version) ++ + Seq("groupId" -> group, "artifactId" -> artifact, "version" -> { if (symbols.contains(version)) s"$${$version}" else version }) ++ classifier.map("classifier" -> _) ++ scope.map("scope" -> _) elements.map { case (element, value) => s" <$element>$value" @@ -769,6 +781,9 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective(" tools.split("[,]").map(_.trim).filter(_.nonEmpty).foreach { tool => val (lang, code) = tool match { case "sbt" => + val symbolVals = symbolPostfixes.map { sp => + s"""val ${requiredCoordinate(VersionSymbol + sp)} = "${requiredCoordinate(VersionValue + sp)}"\n""" + }.mkString val artifacts = dependencyPostfixes.map { dp => sbt( requiredCoordinate(s"group$dp"), @@ -785,9 +800,13 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective(" Seq("libraryDependencies ++= Seq(", artifacts.map(a => s" $a").mkString(",\n"), ")").mkString("\n") } - ("scala", libraryDependencies) + ("scala", symbolVals + libraryDependencies) case "gradle" | "Gradle" => + val symbolProperties = if (symbols.isEmpty) "" else + symbolPostfixes.map { sp => + s""" ${requiredCoordinate(VersionSymbol + sp)}: "${requiredCoordinate(VersionValue + sp)}"""" + }.mkString("versions += [\n", ",\n", "\n]\n") val artifacts = dependencyPostfixes.map { dp => gradle( requiredCoordinate(s"group$dp"), @@ -801,9 +820,14 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective(" val libraryDependencies = Seq("dependencies {", artifacts.map(a => s" $a").mkString(",\n"), "}").mkString("\n") - ("gradle", libraryDependencies) + ("gradle", symbolProperties + libraryDependencies) case "maven" | "Maven" | "mvn" => + val symbolProperties = if (symbols.isEmpty) "" else + symbolPostfixes.map { sp => + val symb = s"""${requiredCoordinate(VersionSymbol + sp)}""" + s""" <$symb>${requiredCoordinate(VersionValue + sp)}</$symb>""" + }.mkString("<properties>\n", "\n", "\n</properties>\n") val artifacts = dependencyPostfixes.map { dp => mvn( requiredCoordinate(s"group$dp"), @@ -814,7 +838,7 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective(" ) } - ("xml", artifacts.mkString("\n")) + ("xml", symbolProperties + artifacts.mkString("\n")) } printer.print(s"""
$tool
""") diff --git a/docs/src/main/paradox/directives/dependencies.md b/docs/src/main/paradox/directives/dependencies.md index 9695a02b..5dbf7a7c 100644 --- a/docs/src/main/paradox/directives/dependencies.md +++ b/docs/src/main/paradox/directives/dependencies.md @@ -44,6 +44,8 @@ Which will render as: classifier="assets" } +## Multiple dependencies + It is also possible to render more than one dependency in a list. In that case library coordinates need to be appended with the same suffix. For example @@ -60,3 +62,43 @@ will be rendered as: group="com.example" artifact="domain" version="0.1.0" group2="com.example" artifact2="another-domain" version2="0.2.1" } + +## Symbolic version numbers + +When multiple dependencies always use the same version, symbolic version names can be shown. For example + +```scala +@@dependency[sbt,Maven,gradle] { + symbol="AkkaVersion" + value="2.5.29" + symbol2="AkkaHttpVersion" + value2="10.1.0" + group="com.typesafe.akka" + artifact="akka-stream_$scala.binary.version$" + version="AkkaVersion" + group2="com.typesafe.akka" + artifact2="akka-actor-typed_$scala.binary.version$" + version2="AkkaVersion" + group3="com.typesafe.akka" + artifact3="akka-http_$scala.binary.version$" + version3="AkkaHttpVersion" +} +``` + +will be rendered as: + +@@dependency[sbt,Maven,gradle] { + symbol="AkkaVersion" + value="2.5.29" + symbol2="AkkaHttpVersion" + value2="10.1.0" + group="com.typesafe.akka" + artifact="akka-stream_$scala.binary.version$" + version="AkkaVersion" + group2="com.typesafe.akka" + artifact2="akka-actor-typed_$scala.binary.version$" + version2="AkkaVersion" + group3="com.typesafe.akka" + artifact3="akka-http_$scala.binary.version$" + version3="AkkaHttpVersion" +} diff --git a/tests/src/test/scala/com/lightbend/paradox/markdown/DependencyDirectiveSpec.scala b/tests/src/test/scala/com/lightbend/paradox/markdown/DependencyDirectiveSpec.scala index 64b92bbf..1a61926c 100644 --- a/tests/src/test/scala/com/lightbend/paradox/markdown/DependencyDirectiveSpec.scala +++ b/tests/src/test/scala/com/lightbend/paradox/markdown/DependencyDirectiveSpec.scala @@ -231,4 +231,111 @@ class DependencyDirectiveSpec extends MarkdownBaseSpec { | """) } + + it should "render symbolic version values" in { + markdown(""" + |@@dependency[sbt,Maven,gradle] { + | symbol="AkkaHttpVersion" + | value="10.1.0" + | group="com.typesafe.akka" + | artifact="akka-http_$scala.binary.version$" + | version="AkkaHttpVersion" + |}""") shouldEqual html(s""" + |
+ |
sbt
+ |
+ |
+      |
+      |val AkkaHttpVersion = "10.1.0"
+      |libraryDependencies += "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion
+ |
+ |
Maven
+ |
+ |
+      |
+      |<properties>
+      |  <AkkaHttpVersion>10.1.0</AkkaHttpVersion>
+      |</properties>
+      |<dependency>
+      |  <groupId>com.typesafe.akka</groupId>
+      |  <artifactId>akka-http_2.12</artifactId>
+      |  <version>$${AkkaHttpVersion}</version>
+      |</dependency>
+ |
+ |
gradle
+ |
+ |
+      |
+      |versions += [
+      |  AkkaHttpVersion: "10.1.0"
+      |]
+      |dependencies {
+      |  compile group: 'com.typesafe.akka', name: 'akka-http_2.12', version: versions.AkkaHttpVersion
+      |}
+      |
+ |
+ |
""") + } + + it should "render multiple symbolic version values" in { + markdown(""" + |@@dependency[sbt,Maven,gradle] { + | symbol="AkkaVersion" + | value="2.5.29" + | symbol2="AkkaHttpVersion" + | value2="10.1.0" + | group="com.typesafe.akka" + | artifact="akka-stream_$scala.binary.version$" + | version="AkkaVersion" + | group2="com.typesafe.akka" + | artifact2="akka-http_$scala.binary.version$" + | version2="AkkaHttpVersion" + |}""") shouldEqual html(s""" + |
+ |
sbt
+ |
+ |
+      |
+      |val AkkaVersion = "2.5.29"
+      |val AkkaHttpVersion = "10.1.0"
+      |libraryDependencies ++= Seq(
+      |  "com.typesafe.akka" %% "akka-stream" % AkkaVersion,
+      |  "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion
+      |)
+ |
+ |
Maven
+ |
+ |
+      |
+      |<properties>
+      |  <AkkaVersion>2.5.29</AkkaVersion>
+      |  <AkkaHttpVersion>10.1.0</AkkaHttpVersion>
+      |</properties>
+      |<dependency>
+      |  <groupId>com.typesafe.akka</groupId>
+      |  <artifactId>akka-stream_2.12</artifactId>
+      |  <version>$${AkkaVersion}</version>
+      |</dependency>
+      |<dependency>
+      |  <groupId>com.typesafe.akka</groupId>
+      |  <artifactId>akka-http_2.12</artifactId>
+      |  <version>$${AkkaHttpVersion}</version>
+      |</dependency>
+ |
+ |
gradle
+ |
+ |
+      |
+      |versions += [
+      |  AkkaVersion: "2.5.29",
+      |  AkkaHttpVersion: "10.1.0"
+      |]
+      |dependencies {
+      |  compile group: 'com.typesafe.akka', name: 'akka-stream_2.12', version: versions.AkkaVersion,
+      |  compile group: 'com.typesafe.akka', name: 'akka-http_2.12', version: versions.AkkaHttpVersion
+      |}
+      |
+ |
+ |
""") + } }