From 5c8598b6cf01ce9a4defd48cab4c78bdd3cf3489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Tue, 18 Aug 2026 14:32:59 +0200 Subject: [PATCH 1/3] Strip -scalajs from scaladoc invocation to fix Scala.js doc/publish crash scaladoc inherits the full project scalacOptions, including -scalajs for Scala.js builds. That flag makes scaladoc run JS backend codegen phases (e.g. JUnitBootstrappers), which crash on otherwise valid code (#3006). The generated doc content is platform-agnostic, so drop the flag before invoking scaladoc. Co-Authored-By: Claude Sonnet 5 --- .../scala/scala/cli/commands/doc/Doc.scala | 6 ++++- .../cli/integration/DocTestDefinitions.scala | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/modules/cli/src/main/scala/scala/cli/commands/doc/Doc.scala b/modules/cli/src/main/scala/scala/cli/commands/doc/Doc.scala index aa7cc96898..49019960c0 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/doc/Doc.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/doc/Doc.scala @@ -273,8 +273,12 @@ object Doc extends ScalaCommand[DocOptions] with BuildCommandHelpers { .getOrElse(true) then defaultScaladocArgs(scalaParams.scalaVersion, javaVersion) else Nil + // scaladoc has its own `-scalajs` flag, but passing it on triggers JS backend + // codegen phases (e.g. JUnitBootstrappers) that crash on otherwise valid code + // (VirtusLab/scala-cli#3006). The doc content is platform-agnostic, so drop it. val args = baseArgs ++ - builds.head.project.scalaCompiler.map(_.scalacOptions).getOrElse(Nil) ++ + builds.head.project.scalaCompiler.map(_.scalacOptions).getOrElse(Nil) + .filterNot(_ == "-scalajs") ++ extraArgs ++ defaultArgs ++ builds.map(_.output.toString) diff --git a/modules/integration/src/test/scala/scala/cli/integration/DocTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/DocTestDefinitions.scala index ccfaa3f3e0..ebd4096040 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/DocTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/DocTestDefinitions.scala @@ -190,4 +190,26 @@ abstract class DocTestDefinitions } } } + + //`-scalajs` triggers JS codegen phases (e.g. JUnitBootstrappers) that crash on valid code + if (!actualScalaVersion.startsWith("2.")) + test("generate scala doc for a Scala.js project") { + val dest = os.rel / "doc-js" + TestInputs( + os.rel / "Hello.scala" -> + """/** Greeter. */ + |object Hello { + | def greet: String = "hi" + |} + |""".stripMargin + ).fromRoot { root => + os.proc(TestUtil.cli, "doc", extraOptions, "--js", ".", "-o", dest).call( + cwd = root, + stdin = os.Inherit, + stdout = os.Inherit + ) + expect(os.isDir(root / dest)) + expect(os.list(root / dest).exists(_.last.endsWith(".html"))) + } + } } From c4a702cd1e0baf88be314ca3749773eb3398bc32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Tue, 18 Aug 2026 14:40:16 +0200 Subject: [PATCH 2/3] fmt --- .../test/scala/scala/cli/integration/DocTestDefinitions.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/integration/src/test/scala/scala/cli/integration/DocTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/DocTestDefinitions.scala index ebd4096040..f80612d740 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/DocTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/DocTestDefinitions.scala @@ -191,7 +191,7 @@ abstract class DocTestDefinitions } } - //`-scalajs` triggers JS codegen phases (e.g. JUnitBootstrappers) that crash on valid code + // `-scalajs` triggers JS codegen phases (e.g. JUnitBootstrappers) that crash on valid code if (!actualScalaVersion.startsWith("2.")) test("generate scala doc for a Scala.js project") { val dest = os.rel / "doc-js" From ffa3254a703b894b46dce85f480a7f5d1ff0c546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Wed, 19 Aug 2026 09:51:18 +0200 Subject: [PATCH 3/3] Log when -scalajs is filtered out of the scaladoc invocation Makes the workaround from the previous commit visible instead of silent. --- .../src/main/scala/scala/cli/commands/doc/Doc.scala | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/cli/src/main/scala/scala/cli/commands/doc/Doc.scala b/modules/cli/src/main/scala/scala/cli/commands/doc/Doc.scala index 49019960c0..e6a6b61435 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/doc/Doc.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/doc/Doc.scala @@ -276,9 +276,16 @@ object Doc extends ScalaCommand[DocOptions] with BuildCommandHelpers { // scaladoc has its own `-scalajs` flag, but passing it on triggers JS backend // codegen phases (e.g. JUnitBootstrappers) that crash on otherwise valid code // (VirtusLab/scala-cli#3006). The doc content is platform-agnostic, so drop it. + val (scalaJsScalacOptions, otherScalacOptions) = builds.head.project.scalaCompiler + .map(_.scalacOptions).getOrElse(Nil) + .partition(_ == "-scalajs") + if scalaJsScalacOptions.nonEmpty then + logger.log( + s"Not passing ${scalaJsScalacOptions.mkString(", ")} to scaladoc, " + + "as it can cause it to crash on valid code (VirtusLab/scala-cli#3006)" + ) val args = baseArgs ++ - builds.head.project.scalaCompiler.map(_.scalacOptions).getOrElse(Nil) - .filterNot(_ == "-scalajs") ++ + otherScalacOptions ++ extraArgs ++ defaultArgs ++ builds.map(_.output.toString)