diff --git a/modules/cli/src/main/scala/scala/cli/commands/package0/Package.scala b/modules/cli/src/main/scala/scala/cli/commands/package0/Package.scala index b1a17a6e95..8adb90cd5c 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/package0/Package.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/package0/Package.scala @@ -720,14 +720,14 @@ object Package extends ScalaCommand[PackageOptions] with BuildCommandHelpers { isFullOpt <- builds.head.options.scalaJsOptions.fullOpt linkerConfig = builds.head.options.scalaJsOptions.linkerConfig(logger) linkResult <- linkJs( - builds, - destPath, - mainClass, + builds = builds, + dest = destPath, + mainClassOpt = mainClass, addTestInitializer = false, - linkerConfig, - isFullOpt, - builds.head.options.scalaJsOptions.noOpt.getOrElse(false), - logger + config = linkerConfig, + fullOpt = isFullOpt, + noOpt = builds.head.options.scalaJsOptions.noOpt.getOrElse(false), + logger = logger ) } yield linkResult @@ -1023,35 +1023,18 @@ object Package extends ScalaCommand[PackageOptions] with BuildCommandHelpers { builds.head.options.archiveCache ) } - val relMainJs = os.rel / "main.js" - val relSourceMapJs = os.rel / "main.js.map" - val mainJs = linkingDir / relMainJs - val sourceMapJs = linkingDir / relSourceMapJs - - if (os.exists(mainJs)) - if ( - os.walk.stream(linkingDir) - .filter(_ != mainJs) - .filter(_ != sourceMapJs) - .headOption - .nonEmpty - ) { - // copy linking dir to dest - os.copy( - linkingDir, - dest, - createFolders = true, - replaceExisting = true, - mergeFolders = true - ) + os.walk.stream(linkingDir).filter(_.ext == "js").toSeq match { + case Seq(sourceJs) if os.isFile(sourceJs) && sourceJs.last.endsWith(".js") => + // there's just one js file to link, so we copy it directly logger.debug( - s"Scala.js linker generate multiple files for js multi-modules. Copy files to $dest directory." + s"Scala.js linker generated single file ${sourceJs.last}. Copying it to $dest" ) - dest / "main.js" - } - else { - os.copy(mainJs, dest, replaceExisting = true) - if (builds.head.options.scalaJsOptions.emitSourceMaps && os.exists(sourceMapJs)) { + val sourceMapJs = os.Path(sourceJs.toString + ".map") + os.copy(sourceJs, dest, replaceExisting = true) + if builds.head.options.scalaJsOptions.emitSourceMaps && os.exists(sourceMapJs) then { + logger.debug( + s"Source maps emission enabled, copying source map file: ${sourceMapJs.last}" + ) val sourceMapDest = builds.head.options.scalaJsOptions.sourceMapsDest.getOrElse(os.Path(s"$dest.map")) val updatedMainJs = ScalaJsLinker.updateSourceMappingURL(dest) @@ -1059,12 +1042,33 @@ object Package extends ScalaCommand[PackageOptions] with BuildCommandHelpers { os.copy(sourceMapJs, sourceMapDest, replaceExisting = true) logger.message(s"Emitted js source maps to: $sourceMapDest") } - dest - } - else { - val found = os.walk(linkingDir).map(_.relativeTo(linkingDir)) - value(Left(new ScalaJsLinkingError(relMainJs, found))) + case _ @Seq(jsSource, _*) => + os.copy( + linkingDir, + dest, + createFolders = true, + replaceExisting = true, + mergeFolders = true + ) + logger.debug( + s"Scala.js linker generated multiple files for js multi-modules. Copied files to $dest directory." + ) + val jsFileToReturn = os.rel / { + mainClassOpt match { + case Some(_) if os.exists(linkingDir / "main.js") => "main.js" + case Some(mc) if os.exists(linkingDir / s"$mc.js") => s"$mc.js" + case _ => jsSource.relativeTo(linkingDir) + } + } + dest / jsFileToReturn + case Nil => + logger.debug("Scala.js linker did not generate any .js files.") + val allFilesInLinkingDir = os.walk(linkingDir).map(_.relativeTo(linkingDir)) + value(Left(new ScalaJsLinkingError( + expected = if mainClassOpt.nonEmpty then "main.js" else ".js", + foundFiles = allFilesInLinkingDir + ))) } } } diff --git a/modules/integration/src/test/scala/scala/cli/integration/PackageTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/PackageTestDefinitions.scala index 59f9557a10..4d5346af2a 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/PackageTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/PackageTestDefinitions.scala @@ -1485,4 +1485,26 @@ abstract class PackageTestDefinitions extends ScalaCliSuite with TestScalaVersio } } } + + if actualScalaVersion.startsWith("3") then + test("package Scala.js without a main method") { + val moduleName = "whatever" + TestInputs( + os.rel / s"$moduleName.scala" -> + s"""import scala.scalajs.js.annotation.* + | + |object $moduleName { + | @JSExportTopLevel(name = "handler", moduleID = "$moduleName") + | def handler(): Unit = { + | println("Hello world!") + | } + |} + |""".stripMargin + ).fromRoot { root => + val res = + os.proc(TestUtil.cli, "package", ".", "--js", "--power", extraOptions) + .call(cwd = root, mergeErrIntoOut = true, stderr = os.Pipe) + expect(res.out.trim().contains(s"$moduleName.js")) + } + } }