From 089126ea726679083f6baabf0913f25ab6586b20 Mon Sep 17 00:00:00 2001 From: lostflydev Date: Thu, 2 Apr 2026 08:44:21 +0500 Subject: [PATCH 1/9] Add WASM support: --wasm flag with Node.js and Deno runtimes Implements scala-cli issue #3316: integrate WebAssembly with Scala CLI. - `--wasm` CLI flag and `//> using wasm` directive to enable WASM output - `--wasm-runtime ` option and `//> using wasmRuntime` directive Supported values: node (default), deno - `--deno-version`, `--wasmtime-version`, `--wasmer-version` options and corresponding directives for pinning runtime versions - **Node.js** (default): runs Scala.js WASM output with `--experimental-wasm-exnref` flag, requires Node.js >= 22 - **Deno**: runs Scala.js WASM output --- build.mill | 2 + .../scala/scala/build/internal/Runner.scala | 146 ++++- .../DirectivesPreprocessingUtils.scala | 3 +- .../scala/cli/commands/fix/BuiltInRules.scala | 1 + .../scala/scala/cli/commands/run/Run.scala | 515 +++++++++++------- .../cli/commands/shared/HelpGroups.scala | 3 +- .../cli/commands/shared/SharedOptions.scala | 41 +- .../cli/commands/shared/WasmOptions.scala | 32 ++ .../cli/internal/WasmRuntimeDownloader.scala | 104 ++++ .../build/errors/DenoNotFoundError.scala | 5 + .../errors/UnsupportedWasmRuntimeError.scala | 3 + .../build/preprocessing/directives/Wasm.scala | 52 ++ .../RunScalaJsTestDefinitions.scala | 277 +++++++++- .../scala/build/options/BuildOptions.scala | 1 + .../scala/build/options/WasmOptions.scala | 26 + .../scala/build/options/WasmRuntime.scala | 54 ++ website/docs/reference/cli-options.md | 26 + website/docs/reference/directives.md | 21 + 18 files changed, 1085 insertions(+), 227 deletions(-) create mode 100644 modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala create mode 100644 modules/cli/src/main/scala/scala/cli/internal/WasmRuntimeDownloader.scala create mode 100644 modules/core/src/main/scala/scala/build/errors/DenoNotFoundError.scala create mode 100644 modules/core/src/main/scala/scala/build/errors/UnsupportedWasmRuntimeError.scala create mode 100644 modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala create mode 100644 modules/options/src/main/scala/scala/build/options/WasmOptions.scala create mode 100644 modules/options/src/main/scala/scala/build/options/WasmRuntime.scala diff --git a/build.mill b/build.mill index 53e2c30df1..e67a2a754a 100644 --- a/build.mill +++ b/build.mill @@ -519,6 +519,8 @@ trait Core extends ScalaCliCrossSbtModule | def toolkitVersionForNative04 = "${Deps.toolkitVersionForNative04}" | def toolkitVersionForNative05 = "${Deps.toolkitVersionForNative05}" | + | def defaultDenoVersion = "2.1.4" + | | def typelevelOrganization = "${Deps.typelevelToolkit.dep.module.organization.value}" | def typelevelToolkitDefaultVersion = "${Deps.typelevelToolkitVersion}" | def typelevelToolkitMaxScalaNative = "${Deps.Versions.maxScalaNativeForTypelevelToolkit}" diff --git a/modules/build/src/main/scala/scala/build/internal/Runner.scala b/modules/build/src/main/scala/scala/build/internal/Runner.scala index 72c1b685d3..4ba52d017b 100644 --- a/modules/build/src/main/scala/scala/build/internal/Runner.scala +++ b/modules/build/src/main/scala/scala/build/internal/Runner.scala @@ -189,6 +189,60 @@ object Runner { run(command, logger, cwd = cwd, extraEnv = extraEnv) } + // Detects the major version of Node.js on PATH; cached for the JVM lifetime (lazy val). + // Returns None if node is not found or version cannot be parsed. + private lazy val nodeMajorVersion: Option[Int] = + try { + val process = new ProcessBuilder("node", "--version") + .redirectErrorStream(true) + .start() + val output = new String(process.getInputStream.readAllBytes()).trim + process.waitFor() + // Node version format: "v22.5.0" -> extract 22 + if (output.startsWith("v")) + output.drop(1).takeWhile(_.isDigit) match { + case s if s.nonEmpty => Some(s.toInt) + case _ => None + } + else None + } + catch { + case _: Exception => None + } + + // Node 24+ (V8 13+) has wasm-exnref enabled by default; older versions need --experimental-wasm-exnref. + private def nodeNeedsWasmFlag: Boolean = + nodeMajorVersion.forall(_ < 24) // true if unknown or < 24 + + // Detects the major version of Deno on PATH; cached for the JVM lifetime (lazy val). + // Returns None if deno is not found or version cannot be parsed. + private lazy val denoMajorVersion: Option[Int] = + try { + val process = new ProcessBuilder("deno", "--version") + .redirectErrorStream(true) + .start() + val output = new String(process.getInputStream.readAllBytes()).trim + process.waitFor() + // Deno version format: "deno 2.1.0 (release, aarch64-apple-darwin)\nv8 13.x\ntypescript 5.x" + // Extract major from first line + val firstLine = output.linesIterator.nextOption().getOrElse("") + val versionStr = firstLine.stripPrefix("deno ").takeWhile(c => c.isDigit || c == '.') + versionStr.takeWhile(_.isDigit) match { + case s if s.nonEmpty => Some(s.toInt) + case _ => None + } + } + catch { + case _: Exception => None + } + + // Deno 2.x+ bundles V8 13+ which has wasm-exnref enabled by default; no flag needed. + private def denoNeedsWasmFlag: Boolean = + denoMajorVersion.flatMap { major => + if (major >= 2) Some(false) // Deno 2.x+ has V8 13+ with wasm-exnref by default + else Some(true) + }.getOrElse(true) // true if unknown + private def endsWithCaseInsensitive(s: String, suffix: String): Boolean = s.length >= suffix.length && s.regionMatches(true, s.length - suffix.length, suffix, 0, suffix.length) @@ -221,11 +275,13 @@ object Runner { def jsCommand( entrypoint: File, args: Seq[String], - jsDom: Boolean = false + jsDom: Boolean = false, + emitWasm: Boolean = false ): Seq[String] = { - val nodePath = findInPath("node").fold("node")(_.toString) - val command = Seq(nodePath, entrypoint.getAbsolutePath) ++ args + val nodePath = findInPath("node").fold("node")(_.toString) + val nodeFlags = if (emitWasm && nodeNeedsWasmFlag) List("--experimental-wasm-exnref") else Nil + val command = Seq(nodePath) ++ nodeFlags ++ Seq(entrypoint.getAbsolutePath) ++ args if (jsDom) // FIXME We'd need to replicate what JSDOMNodeJSEnv does under-the-hood to get the command in that case. @@ -242,14 +298,16 @@ object Runner { allowExecve: Boolean = false, jsDom: Boolean = false, sourceMap: Boolean = false, - esModule: Boolean = false + esModule: Boolean = false, + emitWasm: Boolean = false ): Either[BuildException, Process] = either { val nodePath: String = value(findInPath("node") .map(_.toString) .toRight(NodeNotFoundError())) + val nodeFlags = if (emitWasm && nodeNeedsWasmFlag) List("--experimental-wasm-exnref") else Nil if !jsDom && allowExecve && Execve.available() then { - val command = Seq(nodePath, entrypoint.getAbsolutePath) ++ args + val command = Seq(nodePath) ++ nodeFlags ++ Seq(entrypoint.getAbsolutePath) ++ args logger.log( s"Running ${command.mkString(" ")}", @@ -265,12 +323,25 @@ object Runner { ) sys.error("should not happen") } + else if (emitWasm) { + // For WASM mode with ES modules, run node directly instead of NodeJSEnv. + // NodeJSEnv's stdin piping with "-" doesn't work with Input.ESModule. + val command = Seq(nodePath) ++ nodeFlags ++ Seq(entrypoint.getAbsolutePath) ++ args + + logger.log( + s"Running ${command.mkString(" ")}", + " Running" + System.lineSeparator() + + command.iterator.map(_ + System.lineSeparator()).mkString + ) + + new ProcessBuilder(command: _*).inheritIO().start() + } else { val nodeArgs = // Scala.js runs apps by piping JS to node. // If we need to pass arguments, we must first make the piped input explicit // with "-", and we pass the user's arguments after that. - if args.isEmpty then Nil else "-" :: args.toList + nodeFlags ++ (if args.isEmpty then Nil else "-" :: args.toList) val envJs = if jsDom then new JSDOMNodeJSEnv( @@ -307,6 +378,69 @@ object Runner { } } + def denoCommand( + entrypoint: File, + args: Seq[String], + denoPathOpt: Option[String] = None + ): Seq[String] = { + val denoPath = denoPathOpt.getOrElse(findInPath("deno").fold("deno")(_.toString)) + val denoFlags = Seq("run", "--allow-read") + Seq(denoPath) ++ denoFlags ++ Seq(entrypoint.getAbsolutePath) ++ args + } + + def runDeno( + entrypoint: File, + args: Seq[String], + logger: Logger, + allowExecve: Boolean = false, + emitWasm: Boolean = false, + denoPathOpt: Option[String] = None + ): Either[BuildException, Process] = either { + val denoPath: String = denoPathOpt.getOrElse { + value(findInPath("deno") + .map(_.toString) + .toRight(DenoNotFoundError())) + } + val denoFlags = Seq("run", "--allow-read") + val extraEnv = + if (emitWasm && denoNeedsWasmFlag) Map("DENO_V8_FLAGS" -> "--experimental-wasm-exnref") + else Map.empty + + if (allowExecve && Execve.available()) { + val command = Seq(denoPath) ++ denoFlags ++ Seq(entrypoint.getAbsolutePath) ++ args + + logger.log( + s"Running ${command.mkString(" ")}", + " Running" + System.lineSeparator() + + command.iterator.map(_ + System.lineSeparator()).mkString + ) + + logger.debug("execve available") + Execve.execve( + command.head, + "deno" +: command.tail.toArray, + (sys.env ++ extraEnv).toArray.sorted.map { case (k, v) => s"$k=$v" } + ) + sys.error("should not happen") + } + else { + val command = Seq(denoPath) ++ denoFlags ++ Seq(entrypoint.getAbsolutePath) ++ args + + logger.log( + s"Running ${command.mkString(" ")}", + " Running" + System.lineSeparator() + + command.iterator.map(_ + System.lineSeparator()).mkString + ) + + val builder = new ProcessBuilder(command*) + .inheritIO() + val env = builder.environment() + for ((k, v) <- extraEnv) + env.put(k, v) + builder.start() + } + } + def runNative( launcher: File, args: Seq[String], diff --git a/modules/build/src/main/scala/scala/build/preprocessing/directives/DirectivesPreprocessingUtils.scala b/modules/build/src/main/scala/scala/build/preprocessing/directives/DirectivesPreprocessingUtils.scala index 5b439b07fc..dfacd593fa 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/directives/DirectivesPreprocessingUtils.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/directives/DirectivesPreprocessingUtils.scala @@ -32,7 +32,8 @@ object DirectivesPreprocessingUtils { directives.ScalaVersion.handler, directives.Sources.handler, directives.Watching.handler, - directives.Tests.handler + directives.Tests.handler, + directives.Wasm.handler ).map(_.mapE(_.buildOptions)) val usingDirectiveWithReqsHandlers diff --git a/modules/cli/src/main/scala/scala/cli/commands/fix/BuiltInRules.scala b/modules/cli/src/main/scala/scala/cli/commands/fix/BuiltInRules.scala index 5c34b3368b..a2f92d2e42 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/fix/BuiltInRules.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/fix/BuiltInRules.scala @@ -387,6 +387,7 @@ object BuiltInRules extends CommandHelpers { JavaHome.handler.keys, ScalaNative.handler.keys, ScalaJs.handler.keys, + Wasm.handler.keys, ScalacOptions.handler.keys, JavaOptions.handler.keys, JavacOptions.handler.keys, diff --git a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala index e22e6bedc8..af484d123d 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala @@ -12,13 +12,13 @@ import java.util.concurrent.atomic.AtomicReference import scala.build.* import scala.build.EitherCps.{either, value} import scala.build.Ops.* -import scala.build.errors.{BuildException, CompositeBuildException} +import scala.build.errors.{BuildException, CompositeBuildException, UnsupportedWasmRuntimeError} import scala.build.input.* import scala.build.internal.{Constants, Runner, ScalaJsLinkerConfig} import scala.build.internals.ConsoleUtils.ScalaCliConsole import scala.build.internals.ConsoleUtils.ScalaCliConsole.warnPrefix import scala.build.internals.EnvVar -import scala.build.options.{BuildOptions, JavaOpt, PackageType, Platform, Scope} +import scala.build.options.{BuildOptions, JavaOpt, PackageType, Platform, Scope, WasmRuntime} import scala.cli.CurrentParams import scala.cli.commands.package0.Package import scala.cli.commands.setupide.SetupIde @@ -28,7 +28,7 @@ import scala.cli.commands.util.BuildCommandHelpers.* import scala.cli.commands.util.{BuildCommandHelpers, RunHadoop, RunSpark} import scala.cli.commands.{CommandUtils, ScalaCommand, SpecificationLevel, WatchUtil} import scala.cli.config.Keys -import scala.cli.internal.ProcUtil +import scala.cli.internal.{ProcUtil, WasmRuntimeDownloader} import scala.cli.packaging.Library.fullClassPathMaybeAsJar import scala.cli.util.ArgHelpers.* import scala.cli.util.ConfigDbUtils @@ -474,229 +474,330 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { if shouldLogCrossInfo then logger.debug(s"Running build for ${crossBuildParams.asString}") val build = builds.head either { - build.options.platform.value match { - case Platform.JS => - val esModule = - build.options.scalaJsOptions.moduleKindStr.exists(m => m == "es" || m == "esmodule") - - val linkerConfig = builds.head.options.scalaJsOptions.linkerConfig(logger) - val jsDest = { - val delete = scratchDirOpt.isEmpty - scratchDirOpt.foreach(os.makeDir.all(_)) - os.temp( - dir = scratchDirOpt.orNull, - prefix = "main", - suffix = if esModule then ".mjs" else ".js", - deleteOnExit = delete - ) + val wasmOpts = build.options.wasmOptions + + // Check if WASM mode is requested + if wasmOpts.enabled then { + val runtime = wasmOpts.runtime + + if runtime.isJsBased then { + // JS-based WASM path - uses Scala.js WASM with JavaScript helpers (Node.js or Deno) + val esModule = true // WASM backend uses ES modules + scratchDirOpt.foreach(os.makeDir.all(_)) + val jsDest = os.temp( + dir = scratchDirOpt.orNull, + prefix = "main", + suffix = ".mjs", + deleteOnExit = scratchDirOpt.isEmpty + ) + + // Resolve Deno binary: check PATH first, download if needed + val denoPathOpt: Option[String] = runtime match { + case WasmRuntime.Deno => + val denoCmd = value(WasmRuntimeDownloader.denoCommand( + wasmOpts.finalDenoVersion, + build.options.archiveCache, + logger + )) + Some(denoCmd.head) + case _ => None } - val res = - Package.linkJs( - builds = builds, - dest = jsDest, - mainClassOpt = Some(mainClass), - addTestInitializer = false, - config = linkerConfig, - fullOpt = value(build.options.scalaJsOptions.fullOpt), - noOpt = build.options.scalaJsOptions.noOpt.getOrElse(false), - logger = logger, - scratchDirOpt = scratchDirOpt - ).map { outputPath => - val jsDom = build.options.scalaJsOptions.dom.getOrElse(false) - if showCommand then Left(Runner.jsCommand(outputPath.toIO, args, jsDom = jsDom)) - else { - val process = value { - Runner.runJs( - outputPath.toIO, - args, - logger, - allowExecve = effectiveAllowExecve, - jsDom = jsDom, - sourceMap = build.options.scalaJsOptions.emitSourceMaps, - esModule = esModule - ) - } - process.onExit().thenApply(_ => if os.exists(jsDest) then os.remove(jsDest)) - Right((process, None)) - } - } - value(res) - case Platform.Native => - val setupPython = build.options.notForBloopOptions.doSetupPython.getOrElse(false) - val (pythonExecutable, pythonLibraryPaths, pythonExtraEnv) = - if setupPython then { - val (exec, libPaths) = value { - val python = value(createPythonInstance().orPythonDetectionError) - val pythonPropertiesOrError = for { - paths <- python.nativeLibraryPaths - executable <- python.executable - } yield (Some(executable), paths) - logger.debug( - s"Python executable and native library paths: $pythonPropertiesOrError" - ) - pythonPropertiesOrError.orPythonDetectionError + + val linkerConfig = build.options.scalaJsOptions.linkerConfig(logger) + .copy(emitWasm = true, moduleKind = ScalaJsLinkerConfig.ModuleKind.ESModule) + + val res = Package.linkJs( + builds = builds, + dest = jsDest, + mainClassOpt = Some(mainClass), + addTestInitializer = false, + config = linkerConfig, + fullOpt = value(build.options.scalaJsOptions.fullOpt), + noOpt = build.options.scalaJsOptions.noOpt.getOrElse(false), + logger = logger, + scratchDirOpt = scratchDirOpt + ).map { outputPath => + if showCommand then + runtime match { + case WasmRuntime.Deno => + Left(Runner.denoCommand(outputPath.toIO, args, denoPathOpt = denoPathOpt)) + case _ => + Left(Runner.jsCommand(outputPath.toIO, args, jsDom = false, emitWasm = true)) } - // Putting the workspace in PYTHONPATH, see - // https://github.com/VirtusLab/scala-cli/pull/1616#issuecomment-1333283174 - // for context. - (exec, libPaths, pythonPathEnv(builds.head.inputs.workspace)) - } - else - (None, Nil, Map()) - // seems conda doesn't add the lib directory to LD_LIBRARY_PATH (see conda/conda#308), - // which prevents apps from finding libpython for example, so we update it manually here - val libraryPathsEnv = - if pythonLibraryPaths.isEmpty then Map.empty else { - val prependTo = - if Properties.isWin then EnvVar.Misc.path.name - else if Properties.isMac then EnvVar.Misc.dyldLibraryPath.name - else EnvVar.Misc.ldLibraryPath.name - val currentOpt = Option(System.getenv(prependTo)) - val currentEntries = currentOpt - .map(_.split(File.pathSeparator).toSet) - .getOrElse(Set.empty) - val additionalEntries = pythonLibraryPaths.filter(!currentEntries.contains(_)) - if additionalEntries.isEmpty then Map.empty - else { - val newValue = (additionalEntries.iterator ++ currentOpt.iterator).mkString( - File.pathSeparator - ) - Map(prependTo -> newValue) + val process = value { + runtime match { + case WasmRuntime.Deno => + Runner.runDeno( + outputPath.toIO, + args, + logger, + allowExecve = effectiveAllowExecve, + emitWasm = true, + denoPathOpt = denoPathOpt + ) + case _ => + Runner.runJs( + outputPath.toIO, + args, + logger, + allowExecve = effectiveAllowExecve, + jsDom = false, + sourceMap = build.options.scalaJsOptions.emitSourceMaps, + esModule = esModule, + emitWasm = true + ) + } } + process.onExit().thenApply(_ => if os.exists(jsDest) then os.remove(jsDest)) + Right((process, None)) } - val programNameEnv = - pythonExecutable.fold(Map.empty)(py => Map("SCALAPY_PYTHON_PROGRAMNAME" -> py)) - val extraEnv = libraryPathsEnv ++ programNameEnv ++ pythonExtraEnv - val maybeResult = withNativeLauncher( - builds, - mainClass, - logger - ) { launcher => - if showCommand then - Left( - extraEnv.toVector.sorted.map { case (k, v) => s"$k=$v" } ++ - Seq(launcher.toString) ++ - args - ) - else { - val proc = Runner.runNative( - launcher = launcher.toIO, - args = args, - logger = logger, - allowExecve = effectiveAllowExecve, - extraEnv = extraEnv + } + value(res) + } + else { + // Standalone WASM runtimes - not yet supported. + // Scala.js currently produces JS-dependent WASM output. + // Standalone support requires upstream Scala.js changes (scala-js/scala-js#4991). + val runtimeName = runtime.name + val extraNote = runtime match { + case WasmRuntime.Wasmer => + " Note: Wasmer does not yet support WasmGC, which is required for Scala WASM output." + case _ => "" + } + value(Left(new UnsupportedWasmRuntimeError( + s"Standalone WASM runtime '$runtimeName' is not yet supported." + + s"$extraNote" + + " Scala.js currently produces JavaScript-dependent WASM output." + + " Standalone WASM support is tracked at: https://github.com/scala-js/scala-js/issues/4991" + + " Use --wasm-runtime node (default) or --wasm-runtime deno for JS-based WASM execution." + ))) + } + } + else + build.options.platform.value match { + case Platform.JS => + val esModule = + build.options.scalaJsOptions.moduleKindStr.exists(m => m == "es" || m == "esmodule") + + val linkerConfig = build.options.scalaJsOptions.linkerConfig(logger) + val jsDest = { + val delete = scratchDirOpt.isEmpty + scratchDirOpt.foreach(os.makeDir.all(_)) + os.temp( + dir = scratchDirOpt.orNull, + prefix = "main", + suffix = if esModule then ".mjs" else ".js", + deleteOnExit = delete ) - Right((proc, None)) } - } - value(maybeResult) - case Platform.JVM => - def fwd(s: String): String = s.replace('\\', '/') - def base(s: String): String = fwd(s).replaceAll(".*/", "") - runMode match { - case RunMode.Default => - val sourceFiles = builds.head.inputs.sourceFiles().map { - case s: ScalaFile => fwd(s.path.toString) - case s: Script => fwd(s.path.toString) - case s: MarkdownFile => fwd(s.path.toString) - case _: SbtFile => "" - case s: OnDisk => fwd(s.path.toString) - case null => "" - }.filter(_.nonEmpty).distinct - val sources = sourceFiles.mkString(File.pathSeparator) - val sourceNames = sourceFiles.map(base).mkString(File.pathSeparator) - - val baseJavaProps = build.options.javaOptions.javaOpts.toSeq.map(_.value.value) - ++ Seq(s"-Dscala.sources=$sources", s"-Dscala.source.names=$sourceNames") - val setupPython = build.options.notForBloopOptions.doSetupPython.getOrElse(false) - val (pythonJavaProps, pythonExtraEnv) = - if setupPython then { - val scalapyProps = value { - val python = value(createPythonInstance().orPythonDetectionError) - val propsOrError = python.scalapyProperties - logger.debug(s"Python Java properties: $propsOrError") - propsOrError.orPythonDetectionError - } - val props = scalapyProps.toVector.sorted.map { - case (k, v) => s"-D$k=$v" + val res = + Package.linkJs( + builds = builds, + dest = jsDest, + mainClassOpt = Some(mainClass), + addTestInitializer = false, + config = linkerConfig, + fullOpt = value(build.options.scalaJsOptions.fullOpt), + noOpt = build.options.scalaJsOptions.noOpt.getOrElse(false), + logger = logger, + scratchDirOpt = scratchDirOpt + ).map { outputPath => + val jsDom = build.options.scalaJsOptions.dom.getOrElse(false) + if showCommand then Left(Runner.jsCommand(outputPath.toIO, args, jsDom = jsDom)) + else { + val process = value { + Runner.runJs( + outputPath.toIO, + args, + logger, + allowExecve = effectiveAllowExecve, + jsDom = jsDom, + sourceMap = build.options.scalaJsOptions.emitSourceMaps, + esModule = esModule + ) } - // Putting the workspace in PYTHONPATH, see - // https://github.com/VirtusLab/scala-cli/pull/1616#issuecomment-1333283174 - // for context. - (props, pythonPathEnv(build.inputs.workspace)) + process.onExit().thenApply(_ => if os.exists(jsDest) then os.remove(jsDest)) + Right((process, None)) } - else - (Nil, Map.empty[String, String]) - val allJavaOpts = pythonJavaProps ++ baseJavaProps - if showCommand then - Left { - Runner.jvmCommand( - build.options.javaHome().value.javaCommand, - allJavaOpts, - builds.flatMap(_.fullClassPathMaybeAsJar(asJar)).distinct, - mainClass, - args, - extraEnv = pythonExtraEnv, - useManifest = build.options.notForBloopOptions.runWithManifest, - scratchDirOpt = scratchDirOpt + } + value(res) + case Platform.Native => + val setupPython = build.options.notForBloopOptions.doSetupPython.getOrElse(false) + val (pythonExecutable, pythonLibraryPaths, pythonExtraEnv) = + if setupPython then { + val (exec, libPaths) = value { + val python = value(createPythonInstance().orPythonDetectionError) + val pythonPropertiesOrError = for { + paths <- python.nativeLibraryPaths + executable <- python.executable + } yield (Some(executable), paths) + logger.debug( + s"Python executable and native library paths: $pythonPropertiesOrError" ) + pythonPropertiesOrError.orPythonDetectionError } - else { - val proc = Runner.runJvm( - javaCommand = build.options.javaHome().value.javaCommand, - javaArgs = allJavaOpts, - classPath = builds.flatMap(_.fullClassPathMaybeAsJar(asJar)).distinct, - mainClass = mainClass, - args = args, - logger = logger, - allowExecve = effectiveAllowExecve, - extraEnv = pythonExtraEnv, - useManifest = build.options.notForBloopOptions.runWithManifest, - scratchDirOpt = scratchDirOpt - ) - Right((proc, None)) + // Putting the workspace in PYTHONPATH, see + // https://github.com/VirtusLab/scala-cli/pull/1616#issuecomment-1333283174 + // for context. + (exec, libPaths, pythonPathEnv(builds.head.inputs.workspace)) } - case mode: RunMode.SparkSubmit => - value { - RunSpark.run( - builds = builds, - mainClass = mainClass, - args = args, - submitArgs = mode.submitArgs, - logger = logger, - allowExecve = effectiveAllowExecve, - showCommand = showCommand, - scratchDirOpt = scratchDirOpt - ) + else + (None, Nil, Map()) + // seems conda doesn't add the lib directory to LD_LIBRARY_PATH (see conda/conda#308), + // which prevents apps from finding libpython for example, so we update it manually here + val libraryPathsEnv = + if pythonLibraryPaths.isEmpty then Map.empty + else { + val prependTo = + if Properties.isWin then EnvVar.Misc.path.name + else if Properties.isMac then EnvVar.Misc.dyldLibraryPath.name + else EnvVar.Misc.ldLibraryPath.name + val currentOpt = Option(System.getenv(prependTo)) + val currentEntries = currentOpt + .map(_.split(File.pathSeparator).toSet) + .getOrElse(Set.empty) + val additionalEntries = pythonLibraryPaths.filter(!currentEntries.contains(_)) + if additionalEntries.isEmpty then Map.empty + else { + val newValue = (additionalEntries.iterator ++ currentOpt.iterator).mkString( + File.pathSeparator + ) + Map(prependTo -> newValue) + } } - case mode: RunMode.StandaloneSparkSubmit => - value { - RunSpark.runStandalone( - builds = builds, - mainClass = mainClass, - args = args, - submitArgs = mode.submitArgs, - logger = logger, - allowExecve = effectiveAllowExecve, - showCommand = showCommand, - scratchDirOpt = scratchDirOpt + val programNameEnv = + pythonExecutable.fold(Map.empty)(py => Map("SCALAPY_PYTHON_PROGRAMNAME" -> py)) + val extraEnv = libraryPathsEnv ++ programNameEnv ++ pythonExtraEnv + val maybeResult = withNativeLauncher( + builds, + mainClass, + logger + ) { launcher => + if showCommand then + Left( + extraEnv.toVector.sorted.map { case (k, v) => s"$k=$v" } ++ + Seq(launcher.toString) ++ + args ) - } - case RunMode.HadoopJar => - value { - RunHadoop.run( - builds = builds, - mainClass = mainClass, + else { + val proc = Runner.runNative( + launcher = launcher.toIO, args = args, logger = logger, allowExecve = effectiveAllowExecve, - showCommand = showCommand, - scratchDirOpt = scratchDirOpt + extraEnv = extraEnv ) + Right((proc, None)) } - } - } + } + value(maybeResult) + case Platform.JVM => + def fwd(s: String): String = s.replace('\\', '/') + def base(s: String): String = fwd(s).replaceAll(".*/", "") + runMode match { + case RunMode.Default => + val sourceFiles = builds.head.inputs.sourceFiles().map { + case s: ScalaFile => fwd(s.path.toString) + case s: Script => fwd(s.path.toString) + case s: MarkdownFile => fwd(s.path.toString) + case s: OnDisk => fwd(s.path.toString) + case s => s.getClass.getName + }.filter(_.nonEmpty).distinct + val sources = sourceFiles.mkString(File.pathSeparator) + val sourceNames = sourceFiles.map(base).mkString(File.pathSeparator) + + val baseJavaProps = build.options.javaOptions.javaOpts.toSeq.map(_.value.value) + ++ Seq(s"-Dscala.sources=$sources", s"-Dscala.source.names=$sourceNames") + val setupPython = + build.options.notForBloopOptions.doSetupPython.getOrElse(false) + val (pythonJavaProps, pythonExtraEnv) = + if setupPython then { + val scalapyProps = value { + val python = value(createPythonInstance().orPythonDetectionError) + val propsOrError = python.scalapyProperties + logger.debug(s"Python Java properties: $propsOrError") + propsOrError.orPythonDetectionError + } + val props = scalapyProps.toVector.sorted.map { + case (k, v) => s"-D$k=$v" + } + // Putting the workspace in PYTHONPATH, see + // https://github.com/VirtusLab/scala-cli/pull/1616#issuecomment-1333283174 + // for context. + (props, pythonPathEnv(build.inputs.workspace)) + } + else + (Nil, Map.empty[String, String]) + val allJavaOpts = pythonJavaProps ++ baseJavaProps + if showCommand then + Left { + Runner.jvmCommand( + build.options.javaHome().value.javaCommand, + allJavaOpts, + builds.flatMap(_.fullClassPathMaybeAsJar(asJar)).distinct, + mainClass, + args, + extraEnv = pythonExtraEnv, + useManifest = build.options.notForBloopOptions.runWithManifest, + scratchDirOpt = scratchDirOpt + ) + } + else { + val proc = Runner.runJvm( + javaCommand = build.options.javaHome().value.javaCommand, + javaArgs = allJavaOpts, + classPath = builds.flatMap(_.fullClassPathMaybeAsJar(asJar)).distinct, + mainClass = mainClass, + args = args, + logger = logger, + allowExecve = effectiveAllowExecve, + extraEnv = pythonExtraEnv, + useManifest = build.options.notForBloopOptions.runWithManifest, + scratchDirOpt = scratchDirOpt + ) + Right((proc, None)) + } + case mode: RunMode.SparkSubmit => + value { + RunSpark.run( + builds = builds, + mainClass = mainClass, + args = args, + submitArgs = mode.submitArgs, + logger = logger, + allowExecve = effectiveAllowExecve, + showCommand = showCommand, + scratchDirOpt = scratchDirOpt + ) + } + case mode: RunMode.StandaloneSparkSubmit => + value { + RunSpark.runStandalone( + builds = builds, + mainClass = mainClass, + args = args, + submitArgs = mode.submitArgs, + logger = logger, + allowExecve = effectiveAllowExecve, + showCommand = showCommand, + scratchDirOpt = scratchDirOpt + ) + } + case RunMode.HadoopJar => + value { + RunHadoop.run( + builds = builds, + mainClass = mainClass, + args = args, + logger = logger, + allowExecve = effectiveAllowExecve, + showCommand = showCommand, + scratchDirOpt = scratchDirOpt + ) + } + } + } } } .sequence diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/HelpGroups.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/HelpGroups.scala index 8f6099a324..c943c3c904 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/HelpGroups.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/HelpGroups.scala @@ -17,7 +17,7 @@ enum HelpGroup: Scala, ScalaJs, ScalaNative, Secret, Signing, SuppressWarnings, SourceGenerator, Test, Uninstall, Update, - Watch, Windows, + Wasm, Watch, Windows, Version override def toString: String = this match @@ -30,6 +30,7 @@ enum HelpGroup: case SuppressWarnings => "Suppress warnings" case SourceGenerator => "Source generator" case ProjectVersion => "Project version" + case Wasm => "WebAssembly" case e => e.productPrefix enum HelpCommandGroup: diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala index 71dd29d1f3..c48dbc43bd 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala @@ -57,6 +57,8 @@ final case class SharedOptions( js: ScalaJsOptions = ScalaJsOptions(), @Recurse native: ScalaNativeOptions = ScalaNativeOptions(), + @Recurse + wasmOptions: WasmOptions = WasmOptions(), @Recurse compilationServer: SharedCompilationServerOptions = SharedCompilationServerOptions(), @Recurse @@ -311,6 +313,16 @@ final case class SharedOptions( ) } + private def buildWasmOptions(opts: WasmOptions): options.WasmOptions = { + import opts._ + options.WasmOptions( + enabled = wasm, + runtime = + wasmRuntime.flatMap(options.WasmRuntime.parse).getOrElse(options.WasmRuntime.default), + denoVersion = denoVersion + ) + } + lazy val scalacOptionsFromFiles: List[String] = scalac.argsFiles.flatMap(argFile => ArgSplitter.splitToArgs(os.read(os.Path(argFile.file, os.pwd))) @@ -335,21 +347,27 @@ final case class SharedOptions( case _ => } val parsedPlatform = platform.map(Platform.normalize).flatMap(Platform.parse) - val platformOpt = value { - (parsedPlatform, js.js, native.native) match { - case (Some(p: Platform.JS.type), _, false) => Right(Some(p)) - case (Some(p: Platform.Native.type), false, _) => Right(Some(p)) - case (Some(p: Platform.JVM.type), false, false) => Right(Some(p)) - case (Some(p), _, _) => - val jsSeq = if (js.js) Seq(Platform.JS) else Seq.empty + // WASM mode requires Scala.js platform for compilation + val wasmEnabled = wasmOptions.wasm + val platformOpt = value { + (parsedPlatform, js.js, native.native, wasmEnabled) match { + case (Some(p: Platform.JS.type), _, false, _) => Right(Some(p)) + case (Some(p: Platform.Native.type), false, _, false) => Right(Some(p)) + case (Some(p: Platform.JVM.type), false, false, false) => Right(Some(p)) + case (Some(p), _, _, _) => + val jsSeq = if (js.js || wasmEnabled) Seq(Platform.JS) else Seq.empty val nativeSeq = if (native.native) Seq(Platform.Native) else Seq.empty val platformsSeq = Seq(p) ++ jsSeq ++ nativeSeq Left(new AmbiguousPlatformError(platformsSeq.distinct.map(_.toString))) - case (_, true, true) => + case (_, true, true, _) => Left(new AmbiguousPlatformError(Seq(Platform.JS.toString, Platform.Native.toString))) - case (_, true, _) => Right(Some(Platform.JS)) - case (_, _, true) => Right(Some(Platform.Native)) - case _ => Right(None) + case (_, _, true, true) => + Left(new AmbiguousPlatformError(Seq(Platform.Native.toString, "WASM (requires JS)"))) + case (_, true, _, _) => Right(Some(Platform.JS)) + case (_, _, _, true) => + Right(Some(Platform.JS)) // WASM requires JS compilation (Scala.js WASM backend) + case (_, _, true, _) => Right(Some(Platform.Native)) + case _ => Right(None) } } val (assumedSourceJars, extraRegularJarsAndClasspath) = @@ -436,6 +454,7 @@ final case class SharedOptions( ), scalaJsOptions = scalaJsOptions(js), scalaNativeOptions = snOpts, + wasmOptions = buildWasmOptions(wasmOptions), javaOptions = value(scala.cli.commands.util.JvmUtils.javaOptions(jvm)), jmhOptions = scala.build.options.JmhOptions( jmhVersion = benchmarking.jmhVersion, diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala new file mode 100644 index 0000000000..d8793afaa2 --- /dev/null +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala @@ -0,0 +1,32 @@ +package scala.cli.commands.shared + +import caseapp.* +import com.github.plokhotnyuk.jsoniter_scala.core.* +import com.github.plokhotnyuk.jsoniter_scala.macros.* + +import scala.cli.commands.tags + +// format: off +final case class WasmOptions( + @Group(HelpGroup.Scala.toString) + @Tag(tags.experimental) + @HelpMessage("Enable WebAssembly output (Scala.js WASM backend). Uses Node.js by default. To show more options for WASM pass `--help-wasm`") + wasm: Boolean = false, + + @Group(HelpGroup.Wasm.toString) + @Tag(tags.experimental) + @HelpMessage("WASM runtime to use: node (default), deno. Standalone runtimes (wasmtime, wasmedge) planned for future releases.") + wasmRuntime: Option[String] = None, + + @Group(HelpGroup.Wasm.toString) + @Tag(tags.experimental) + @HelpMessage("Version of Deno to use. If Deno is not found on PATH, it will be downloaded automatically.") + denoVersion: Option[String] = None +) +// format: on + +object WasmOptions { + implicit lazy val parser: Parser[WasmOptions] = Parser.derive + implicit lazy val help: Help[WasmOptions] = Help.derive + implicit lazy val jsonCodec: JsonValueCodec[WasmOptions] = JsonCodecMaker.make +} diff --git a/modules/cli/src/main/scala/scala/cli/internal/WasmRuntimeDownloader.scala b/modules/cli/src/main/scala/scala/cli/internal/WasmRuntimeDownloader.scala new file mode 100644 index 0000000000..751e087034 --- /dev/null +++ b/modules/cli/src/main/scala/scala/cli/internal/WasmRuntimeDownloader.scala @@ -0,0 +1,104 @@ +package scala.cli.internal + +import coursier.cache.ArchiveCache +import coursier.util.Task + +import java.util.Locale + +import scala.build.EitherCps.{either, value} +import scala.build.Logger +import scala.build.errors.BuildException +import scala.build.internal.FetchExternalBinary +import scala.util.Properties + +/** Resolves Deno binary for WASM execution. + * + * Deno is first looked up on the system PATH. If not found, it is downloaded from GitHub releases + * and cached via Coursier's ArchiveCache. + */ +object WasmRuntimeDownloader { + + /** Returns the command to run Deno. + * + * First checks system PATH, otherwise downloads the binary. + */ + def denoCommand( + version: String, + archiveCache: ArchiveCache[Task], + logger: Logger + ): Either[BuildException, Seq[String]] = either { + findOnPath("deno") match { + case Some(path) => + logger.debug(s"Using system deno at: $path") + Seq(path) + case None => + logger.message(s"Deno not found on PATH, downloading v$version...") + val binary = value(fetchDeno(version, archiveCache, logger)) + Seq(binary.toString) + } + } + + /** Find an executable on the system PATH */ + private def findOnPath(name: String): Option[String] = { + val exeName = if (Properties.isWin) s"$name.exe" else name + sys.env.get("PATH").flatMap { pathEnv => + pathEnv.split(java.io.File.pathSeparator).view.map { dir => + val file = new java.io.File(dir, exeName) + if (file.exists() && file.canExecute) Some(file.getAbsolutePath) + else None + }.find(_.isDefined).flatten + } + } + + private def detectOs(win: String, linux: String, mac: String): Either[BuildException, String] = + if (Properties.isWin) Right(win) + else if (Properties.isLinux) Right(linux) + else if (Properties.isMac) Right(mac) + else Left(new WasmRuntimeDownloadError(s"Unsupported OS: ${sys.props("os.name")}")) + + private def detectArch64(x86_64: String, aarch64: String): Either[BuildException, String] = + sys.props("os.arch").toLowerCase(Locale.ROOT) match { + case "amd64" | "x86_64" => Right(x86_64) + case "aarch64" | "arm64" => Right(aarch64) + case other => Left(new WasmRuntimeDownloadError(s"Unsupported architecture: $other")) + } + + /** Fetches Deno binary for the current platform. + * + * Deno releases are at: + * https://github.com/denoland/deno/releases/download/v{version}/deno-{platform}.zip + */ + private def fetchDeno( + version: String, + archiveCache: ArchiveCache[Task], + logger: Logger + ): Either[BuildException, os.Path] = either { + val platform = value(denoPlatform) + val url = s"https://github.com/denoland/deno/releases/download/v$version/deno-$platform.zip" + + val binaryOpt = value { + FetchExternalBinary.fetchLauncher( + url = url, + changing = false, + archiveCache = archiveCache, + logger = logger, + launcherPrefix = "deno", + launcherPathOpt = None, + makeExecutable = true + ) + } + + binaryOpt.getOrElse { + value(Left(new WasmRuntimeDownloadError(s"Could not download Deno v$version for $platform"))) + } + } + + /** Platform suffix for Deno downloads */ + private def denoPlatform: Either[BuildException, String] = either { + val arch = value(detectArch64("x86_64", "aarch64")) + val os = value(detectOs("pc-windows-msvc", "unknown-linux-gnu", "apple-darwin")) + s"$arch-$os" + } +} + +class WasmRuntimeDownloadError(message: String) extends BuildException(message) diff --git a/modules/core/src/main/scala/scala/build/errors/DenoNotFoundError.scala b/modules/core/src/main/scala/scala/build/errors/DenoNotFoundError.scala new file mode 100644 index 0000000000..4566e346a4 --- /dev/null +++ b/modules/core/src/main/scala/scala/build/errors/DenoNotFoundError.scala @@ -0,0 +1,5 @@ +package scala.build.errors + +final class DenoNotFoundError extends BuildException( + "Deno was not found on the PATH. Install Deno from https://deno.land/ or use --wasm-runtime node" + ) diff --git a/modules/core/src/main/scala/scala/build/errors/UnsupportedWasmRuntimeError.scala b/modules/core/src/main/scala/scala/build/errors/UnsupportedWasmRuntimeError.scala new file mode 100644 index 0000000000..b663b6a956 --- /dev/null +++ b/modules/core/src/main/scala/scala/build/errors/UnsupportedWasmRuntimeError.scala @@ -0,0 +1,3 @@ +package scala.build.errors + +final class UnsupportedWasmRuntimeError(message: String) extends BuildException(message) diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala new file mode 100644 index 0000000000..361fcc32ab --- /dev/null +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala @@ -0,0 +1,52 @@ +package scala.build.preprocessing.directives + +import scala.build.Positioned +import scala.build.directives.* +import scala.build.errors.BuildException +import scala.build.options.{BuildOptions, Platform, ScalaOptions, WasmOptions, WasmRuntime} +import scala.cli.commands.SpecificationLevel + +@DirectiveGroupName("WASM options") +@DirectiveExamples("//> using wasm") +@DirectiveExamples("//> using wasmRuntime node") +@DirectiveExamples("//> using wasmRuntime deno") +@DirectiveExamples("//> using denoVersion 2.1.4") +@DirectiveUsage( + "//> using wasm|wasmRuntime|denoVersion _value_", + """ + |`//> using wasm` _true|false_ + | + |`//> using wasm` + | + |`//> using wasmRuntime` _node|deno|wasmtime|wasmedge|wasmer_ + | + |`//> using denoVersion` _value_ + |""".stripMargin +) +@DirectiveDescription("Add WebAssembly options") +@DirectiveLevel(SpecificationLevel.EXPERIMENTAL) +final case class Wasm( + wasm: Option[Boolean] = None, + wasmRuntime: Option[String] = None, + denoVersion: Option[String] = None +) extends HasBuildOptions { + def buildOptions: Either[BuildException, BuildOptions] = { + val parsedRuntime = wasmRuntime.flatMap(WasmRuntime.parse) + val wasmOptions = WasmOptions( + enabled = wasm.getOrElse(false), + runtime = parsedRuntime.getOrElse(WasmRuntime.default), + denoVersion = denoVersion + ) + // When WASM is enabled, force Platform.JS (Scala.js WASM backend requires JS compilation) + val scalaOptions = + if (wasm.getOrElse(false)) + ScalaOptions(platform = Some(Positioned.none(Platform.JS))) + else + ScalaOptions() + Right(BuildOptions(scalaOptions = scalaOptions, wasmOptions = wasmOptions)) + } +} + +object Wasm { + val handler: DirectiveHandler[Wasm] = DirectiveHandler.derive +} diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala index 4745787924..8436bc2e0f 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala @@ -325,11 +325,286 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => .call(cwd = root).out.trim() val path = absOutDir / "main.wasm" expect(os.exists(path)) + } + } + + test("Run with --wasm flag") { + val inputs = TestInputs( + os.rel / "Hello.scala" -> + """object Hello { + | def main(args: Array[String]): Unit = println("Hello from WASM!") + |} + |""".stripMargin + ) + inputs.fromRoot { root => + val output = os.proc( + TestUtil.cli, + "--power", + "run", + "Hello.scala", + "--wasm", + "--wasm-runtime", + "node", + extraOptions + ).call(cwd = root).out.trim() + expect(output == "Hello from WASM!") + } + } - // TODO : Run WASM using node. Requires node 22. + test("Run with --wasm uses Node.js by default") { + val inputs = TestInputs( + os.rel / "Hello.scala" -> + """object Hello { + | def main(args: Array[String]): Unit = println("Hello default WASM!") + |} + |""".stripMargin + ) + inputs.fromRoot { root => + val output = os.proc( + TestUtil.cli, + "--power", + "run", + "Hello.scala", + "--wasm", + extraOptions + ).call(cwd = root).out.trim() + expect(output == "Hello default WASM!") } } + test("Run with //> using wasm directive") { + val inputs = TestInputs( + os.rel / "Hello.scala" -> + """//> using wasm + |//> using wasmRuntime node + |object Hello { + | def main(args: Array[String]): Unit = println("Hello from WASM directive!") + |} + |""".stripMargin + ) + inputs.fromRoot { root => + val output = os.proc( + TestUtil.cli, + "--power", + "run", + "Hello.scala", + extraOptions + ).call(cwd = root).out.trim() + expect(output == "Hello from WASM directive!") + } + } + + test("WASM passes arguments to program") { + // Scala.js always passes an empty Array[String] to main(args), + // so we must read process.argv directly via JS interop. + val inputs = TestInputs( + os.rel / "Hello.scala" -> + """import scala.scalajs.js + |import scala.scalajs.js.Dynamic.global + |object Hello { + | def main(args: Array[String]): Unit = { + | val argv = global.process.argv.asInstanceOf[js.Array[String]].drop(2).toSeq + | println(argv.mkString(" ")) + | } + |} + |""".stripMargin + ) + inputs.fromRoot { root => + val output = os.proc( + TestUtil.cli, + "--power", + "run", + "Hello.scala", + "--wasm", + "--wasm-runtime", + "node", + extraOptions, + "--", + "foo", + "bar", + "baz" + ).call(cwd = root).out.trim() + expect(output == "foo bar baz") + } + } + + for (runtime <- Seq("wasmtime", "wasmedge", "wasmer")) + test(s"Unsupported WASM runtime '$runtime' gives clear error") { + val inputs = TestInputs( + os.rel / "Hello.scala" -> + """object Hello { + | def main(args: Array[String]): Unit = println("Hello!") + |} + |""".stripMargin + ) + inputs.fromRoot { root => + val res = os.proc( + TestUtil.cli, + "--power", + "run", + "Hello.scala", + "--wasm", + "--wasm-runtime", + runtime, + extraOptions + ).call(cwd = root, check = false, mergeErrIntoOut = true) + expect(res.exitCode != 0) + expect(res.out.trim().contains("not yet supported")) + expect(res.out.trim().contains("scala-js/scala-js/issues/4991")) + } + } + + if (TestUtil.fromPath("deno").isDefined) + test("Run with --wasm-runtime deno") { + val inputs = TestInputs( + os.rel / "Hello.scala" -> + """object Hello { + | def main(args: Array[String]): Unit = println("Hello from Deno WASM!") + |} + |""".stripMargin + ) + inputs.fromRoot { root => + val output = os.proc( + TestUtil.cli, + "--power", + "run", + "Hello.scala", + "--wasm", + "--wasm-runtime", + "deno", + extraOptions + ).call(cwd = root).out.trim() + expect(output == "Hello from Deno WASM!") + } + } + + test("WASM multiple source files") { + val inputs = TestInputs( + os.rel / "Greeter.scala" -> + """trait Greeter { + | def greet(name: String): String + |} + | + |object EnthusiasticGreeter extends Greeter { + | def greet(name: String): String = s"Hello, $name!" + |} + |""".stripMargin, + os.rel / "Main.scala" -> + """object Main { + | def main(args: Array[String]): Unit = { + | println(EnthusiasticGreeter.greet("WASM")) + | } + |} + |""".stripMargin + ) + inputs.fromRoot { root => + val output = os.proc( + TestUtil.cli, + "--power", + "run", + "Main.scala", + "Greeter.scala", + "--wasm", + "--wasm-runtime", + "node", + extraOptions + ).call(cwd = root).out.trim() + expect(output == "Hello, WASM!") + } + } + + test("WASM exception handling") { + val inputs = TestInputs( + os.rel / "Hello.scala" -> + """object Hello { + | def riskyOp(x: Int): Int = + | if (x == 0) throw new IllegalArgumentException("zero!") + | else 100 / x + | + | def main(args: Array[String]): Unit = { + | val ok = try riskyOp(5).toString catch { case e: Exception => s"err: ${e.getMessage}" } + | val caught = try riskyOp(0).toString catch { case e: Exception => s"caught: ${e.getMessage}" } + | println(ok) + | println(caught) + | } + |} + |""".stripMargin + ) + inputs.fromRoot { root => + val output = os.proc( + TestUtil.cli, + "--power", + "run", + "Hello.scala", + "--wasm", + "--wasm-runtime", + "node", + extraOptions + ).call(cwd = root).out.trim() + val lines = output.linesIterator.toSeq + expect(lines.contains("20")) + expect(lines.contains("caught: zero!")) + } + } + + test("WASM collections and higher-order functions") { + val inputs = TestInputs( + os.rel / "Hello.scala" -> + """object Hello { + | def fib(n: Int): Int = if (n <= 1) n else fib(n - 1) + fib(n - 2) + | + | def main(args: Array[String]): Unit = { + | val fibs = (0 to 7).map(fib).toList + | println(fibs.mkString(", ")) + | println(fibs.filter(_ % 2 == 0).sum) + | println(fibs.foldLeft(0)(_ + _)) + | } + |} + |""".stripMargin + ) + inputs.fromRoot { root => + val output = os.proc( + TestUtil.cli, + "--power", + "run", + "Hello.scala", + "--wasm", + "--wasm-runtime", + "node", + extraOptions + ).call(cwd = root).out.trim() + val lines = output.linesIterator.toSeq + expect(lines.contains("0, 1, 1, 2, 3, 5, 8, 13")) + expect(lines.contains("10")) // 0 + 2 + 8 = 10 + expect(lines.contains("33")) // sum of first 8 fibs + } + } + + if (!actualScalaVersion.startsWith("2")) + test("WASM @main annotation (Scala 3)") { + // Scala.js always passes empty args to main, so @main with parameters won't work. + // Test @main without parameters instead. + val inputs = TestInputs( + os.rel / "Hello.scala" -> + """@main def hello(): Unit = + | println("Hello, Scala3!") + |""".stripMargin + ) + inputs.fromRoot { root => + val output = os.proc( + TestUtil.cli, + "--power", + "run", + "Hello.scala", + "--wasm", + "--wasm-runtime", + "node", + extraOptions + ).call(cwd = root).out.trim() + expect(output == "Hello, Scala3!") + } + } + test("remap imports directive") { val importmapFile = "importmap.json" val outDir = "out" diff --git a/modules/options/src/main/scala/scala/build/options/BuildOptions.scala b/modules/options/src/main/scala/scala/build/options/BuildOptions.scala index f0f09bf26b..9bc4a8b006 100644 --- a/modules/options/src/main/scala/scala/build/options/BuildOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/BuildOptions.scala @@ -37,6 +37,7 @@ final case class BuildOptions( scalaOptions: ScalaOptions = ScalaOptions(), scalaJsOptions: ScalaJsOptions = ScalaJsOptions(), scalaNativeOptions: ScalaNativeOptions = ScalaNativeOptions(), + wasmOptions: WasmOptions = WasmOptions(), internalDependencies: InternalDependenciesOptions = InternalDependenciesOptions(), javaOptions: JavaOptions = JavaOptions(), jmhOptions: JmhOptions = JmhOptions(), diff --git a/modules/options/src/main/scala/scala/build/options/WasmOptions.scala b/modules/options/src/main/scala/scala/build/options/WasmOptions.scala new file mode 100644 index 0000000000..f96d697803 --- /dev/null +++ b/modules/options/src/main/scala/scala/build/options/WasmOptions.scala @@ -0,0 +1,26 @@ +package scala.build.options + +import scala.build.internal.Constants + +/** Options for WebAssembly compilation and execution. + * + * @param enabled + * If true, enable WASM output (Scala.js WASM backend) + * @param runtime + * The WASM runtime to use for execution (node, deno, wasmtime, wasmedge, wasmer) + * @param denoVersion + * Version of Deno to download (if not found on PATH) + */ +final case class WasmOptions( + enabled: Boolean = false, + runtime: WasmRuntime = WasmRuntime.default, + denoVersion: Option[String] = None +) { + def finalDenoVersion: String = + denoVersion.filter(_.nonEmpty).getOrElse(Constants.defaultDenoVersion) +} + +object WasmOptions { + implicit val hasHashData: HasHashData[WasmOptions] = HasHashData.derive + implicit val monoid: ConfigMonoid[WasmOptions] = ConfigMonoid.derive +} diff --git a/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala b/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala new file mode 100644 index 0000000000..f88f3028ab --- /dev/null +++ b/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala @@ -0,0 +1,54 @@ +package scala.build.options + +import java.util.Locale + +/** Represents available WebAssembly runtimes for execution. + * + * JS-based runtimes (work now with Scala.js WASM backend): + * - Node: Uses Node.js (V8 engine) with JavaScript loader + * - Deno: Uses Deno (V8 engine) with ES module support + * + * Standalone runtimes (future, requires upstream Scala.js standalone WASM support): + * - Wasmtime: Primary standalone target, full WasmGC + Component Model + * - WasmEdge: Secondary standalone target, CNCF cloud-native runtime + * - Wasmer: Placeholder, no WasmGC support yet + */ +sealed abstract class WasmRuntime(val name: String) { + def isJsBased: Boolean = this match { + case WasmRuntime.Node | WasmRuntime.Deno => true + case _ => false + } + def isStandalone: Boolean = !isJsBased +} + +object WasmRuntime { + // JS-based runtimes (work now) + case object Node extends WasmRuntime("node") + case object Deno extends WasmRuntime("deno") + // Standalone runtimes (future - requires upstream Scala.js standalone WASM support) + case object Wasmtime extends WasmRuntime("wasmtime") + case object WasmEdge extends WasmRuntime("wasmedge") + case object Wasmer extends WasmRuntime("wasmer") + + val all: Seq[WasmRuntime] = Seq(Node, Deno, Wasmtime, WasmEdge, Wasmer) + + def default: WasmRuntime = Node + + def parse(s: String): Option[WasmRuntime] = + s.trim.toLowerCase(Locale.ROOT) match { + case "node" | "nodejs" => Some(Node) + case "deno" => Some(Deno) + case "wasmtime" => Some(Wasmtime) + case "wasmedge" => Some(WasmEdge) + case "wasmer" => Some(Wasmer) + case _ => None + } + + implicit val hashedType: HashedType[WasmRuntime] = runtime => runtime.name + + implicit val hasHashData: HasHashData[WasmRuntime] = HasHashData.asIs + + implicit val monoid: ConfigMonoid[WasmRuntime] = ConfigMonoid.instance[WasmRuntime](default) { + (a, b) => if (b == default) a else b + } +} diff --git a/website/docs/reference/cli-options.md b/website/docs/reference/cli-options.md index 0c7e9b1470..fff9d3918f 100644 --- a/website/docs/reference/cli-options.md +++ b/website/docs/reference/cli-options.md @@ -1966,6 +1966,32 @@ A github token used to access GitHub. Not needed in most cases. Don't check for the newest available Scala CLI version upstream +## WebAssembly options + +Available in commands: + +[`run`](./commands.md#run), [`shebang`](./commands.md#shebang) + + + +### `--wasm` + +[Experimental] + +Enable WebAssembly output (Scala.js WASM backend). Uses Node.js by default. To show more options for WASM pass `--help-wasm` + +### `--wasm-runtime` + +[Experimental] + +WASM runtime to use: node (default), deno. Standalone runtimes (wasmtime, wasmedge) planned for future releases. + +### `--deno-version` + +[Experimental] + +Version of Deno to use. If Deno is not found on PATH, it will be downloaded automatically. + ## Watch options Available in commands: diff --git a/website/docs/reference/directives.md b/website/docs/reference/directives.md index d12254bca1..0e1894dde5 100644 --- a/website/docs/reference/directives.md +++ b/website/docs/reference/directives.md @@ -680,6 +680,27 @@ Add Scala.js options `//> using jsEmitWasm` +### WebAssembly + +Add WebAssembly options + +`//> using wasm` _true|false_ + +`//> using wasm` + +`//> using wasmRuntime` _node|deno|wasmtime|wasmedge|wasmer_ + +`//> using denoVersion` _value_ + +#### Examples +`//> using wasm` + +`//> using wasmRuntime node` + +`//> using wasmRuntime deno` + +`//> using denoVersion 2.1.4` + ### Test framework Set the test framework From ddc19f60512eadd3cb0454e41754148343912a12 Mon Sep 17 00:00:00 2001 From: lostflydev Date: Thu, 2 Apr 2026 08:44:21 +0500 Subject: [PATCH 2/9] Review fixes: remove runtime download and unsupported standalone runtimes - Move --wasm flag to dedicated Wasm help group with --help-wasm option - Simplify wasmOptions parsing with fold/toRight pattern - Add runtime validation with UnrecognizedWasmRuntimeError in directives - Auto-enable WASM when wasmRuntime directive is set - Update reference documentation Code style: simplify denoNeedsWasmFlag, explicit runtime match cases, clean type annotation, scalfmt --- build.mill | 2 - .../scala/scala/build/internal/Runner.scala | 16 +- .../scala/scala/cli/commands/run/Run.scala | 145 +++++++----------- .../commands/shared/HelpGroupOptions.scala | 9 +- .../cli/commands/shared/SharedOptions.scala | 26 +++- .../cli/commands/shared/WasmOptions.scala | 11 +- .../cli/internal/WasmRuntimeDownloader.scala | 104 ------------- .../errors/UnrecognizedWasmRuntimeError.scala | 4 + .../errors/UnsupportedWasmRuntimeError.scala | 3 - .../build/preprocessing/directives/Wasm.scala | 46 +++--- .../RunScalaJsTestDefinitions.scala | 26 ---- .../scala/build/options/WasmOptions.scala | 14 +- .../scala/build/options/WasmRuntime.scala | 23 +-- website/docs/reference/cli-options.md | 22 ++- website/docs/reference/commands.md | 30 ++-- website/docs/reference/directives.md | 40 +++-- .../reference/scala-command/cli-options.md | 18 +++ .../docs/reference/scala-command/commands.md | 18 +-- .../scala-command/runner-specification.md | 54 +++++++ 19 files changed, 247 insertions(+), 364 deletions(-) delete mode 100644 modules/cli/src/main/scala/scala/cli/internal/WasmRuntimeDownloader.scala create mode 100644 modules/core/src/main/scala/scala/build/errors/UnrecognizedWasmRuntimeError.scala delete mode 100644 modules/core/src/main/scala/scala/build/errors/UnsupportedWasmRuntimeError.scala diff --git a/build.mill b/build.mill index e67a2a754a..53e2c30df1 100644 --- a/build.mill +++ b/build.mill @@ -519,8 +519,6 @@ trait Core extends ScalaCliCrossSbtModule | def toolkitVersionForNative04 = "${Deps.toolkitVersionForNative04}" | def toolkitVersionForNative05 = "${Deps.toolkitVersionForNative05}" | - | def defaultDenoVersion = "2.1.4" - | | def typelevelOrganization = "${Deps.typelevelToolkit.dep.module.organization.value}" | def typelevelToolkitDefaultVersion = "${Deps.typelevelToolkitVersion}" | def typelevelToolkitMaxScalaNative = "${Deps.Versions.maxScalaNativeForTypelevelToolkit}" diff --git a/modules/build/src/main/scala/scala/build/internal/Runner.scala b/modules/build/src/main/scala/scala/build/internal/Runner.scala index 4ba52d017b..dc8e274fa6 100644 --- a/modules/build/src/main/scala/scala/build/internal/Runner.scala +++ b/modules/build/src/main/scala/scala/build/internal/Runner.scala @@ -238,10 +238,7 @@ object Runner { // Deno 2.x+ bundles V8 13+ which has wasm-exnref enabled by default; no flag needed. private def denoNeedsWasmFlag: Boolean = - denoMajorVersion.flatMap { major => - if (major >= 2) Some(false) // Deno 2.x+ has V8 13+ with wasm-exnref by default - else Some(true) - }.getOrElse(true) // true if unknown + denoMajorVersion.forall(_ < 2) // true if unknown or < 2 private def endsWithCaseInsensitive(s: String, suffix: String): Boolean = s.length >= suffix.length && @@ -380,10 +377,9 @@ object Runner { def denoCommand( entrypoint: File, - args: Seq[String], - denoPathOpt: Option[String] = None + args: Seq[String] ): Seq[String] = { - val denoPath = denoPathOpt.getOrElse(findInPath("deno").fold("deno")(_.toString)) + val denoPath = findInPath("deno").fold("deno")(_.toString) val denoFlags = Seq("run", "--allow-read") Seq(denoPath) ++ denoFlags ++ Seq(entrypoint.getAbsolutePath) ++ args } @@ -393,14 +389,12 @@ object Runner { args: Seq[String], logger: Logger, allowExecve: Boolean = false, - emitWasm: Boolean = false, - denoPathOpt: Option[String] = None + emitWasm: Boolean = false ): Either[BuildException, Process] = either { - val denoPath: String = denoPathOpt.getOrElse { + val denoPath: String = value(findInPath("deno") .map(_.toString) .toRight(DenoNotFoundError())) - } val denoFlags = Seq("run", "--allow-read") val extraEnv = if (emitWasm && denoNeedsWasmFlag) Map("DENO_V8_FLAGS" -> "--experimental-wasm-exnref") diff --git a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala index af484d123d..c8253ee14e 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala @@ -12,7 +12,7 @@ import java.util.concurrent.atomic.AtomicReference import scala.build.* import scala.build.EitherCps.{either, value} import scala.build.Ops.* -import scala.build.errors.{BuildException, CompositeBuildException, UnsupportedWasmRuntimeError} +import scala.build.errors.{BuildException, CompositeBuildException} import scala.build.input.* import scala.build.internal.{Constants, Runner, ScalaJsLinkerConfig} import scala.build.internals.ConsoleUtils.ScalaCliConsole @@ -28,7 +28,7 @@ import scala.cli.commands.util.BuildCommandHelpers.* import scala.cli.commands.util.{BuildCommandHelpers, RunHadoop, RunSpark} import scala.cli.commands.{CommandUtils, ScalaCommand, SpecificationLevel, WatchUtil} import scala.cli.config.Keys -import scala.cli.internal.{ProcUtil, WasmRuntimeDownloader} +import scala.cli.internal.ProcUtil import scala.cli.packaging.Library.fullClassPathMaybeAsJar import scala.cli.util.ArgHelpers.* import scala.cli.util.ConfigDbUtils @@ -478,101 +478,66 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { // Check if WASM mode is requested if wasmOpts.enabled then { - val runtime = wasmOpts.runtime - - if runtime.isJsBased then { - // JS-based WASM path - uses Scala.js WASM with JavaScript helpers (Node.js or Deno) - val esModule = true // WASM backend uses ES modules - scratchDirOpt.foreach(os.makeDir.all(_)) - val jsDest = os.temp( - dir = scratchDirOpt.orNull, - prefix = "main", - suffix = ".mjs", - deleteOnExit = scratchDirOpt.isEmpty - ) - - // Resolve Deno binary: check PATH first, download if needed - val denoPathOpt: Option[String] = runtime match { - case WasmRuntime.Deno => - val denoCmd = value(WasmRuntimeDownloader.denoCommand( - wasmOpts.finalDenoVersion, - build.options.archiveCache, - logger - )) - Some(denoCmd.head) - case _ => None - } + val runtime = wasmOpts.runtime + val esModule = true // WASM backend uses ES modules + scratchDirOpt.foreach(os.makeDir.all(_)) + val jsDest = os.temp( + dir = scratchDirOpt.orNull, + prefix = "main", + suffix = ".mjs", + deleteOnExit = scratchDirOpt.isEmpty + ) - val linkerConfig = build.options.scalaJsOptions.linkerConfig(logger) - .copy(emitWasm = true, moduleKind = ScalaJsLinkerConfig.ModuleKind.ESModule) - - val res = Package.linkJs( - builds = builds, - dest = jsDest, - mainClassOpt = Some(mainClass), - addTestInitializer = false, - config = linkerConfig, - fullOpt = value(build.options.scalaJsOptions.fullOpt), - noOpt = build.options.scalaJsOptions.noOpt.getOrElse(false), - logger = logger, - scratchDirOpt = scratchDirOpt - ).map { outputPath => - if showCommand then + val linkerConfig = build.options.scalaJsOptions.linkerConfig(logger) + .copy(emitWasm = true, moduleKind = ScalaJsLinkerConfig.ModuleKind.ESModule) + + val res = Package.linkJs( + builds = builds, + dest = jsDest, + mainClassOpt = Some(mainClass), + addTestInitializer = false, + config = linkerConfig, + fullOpt = value(build.options.scalaJsOptions.fullOpt), + noOpt = build.options.scalaJsOptions.noOpt.getOrElse(false), + logger = logger, + scratchDirOpt = scratchDirOpt + ).map { outputPath => + if showCommand then + runtime match { + case WasmRuntime.Deno => + Left(Runner.denoCommand(outputPath.toIO, args)) + case WasmRuntime.Node => + Left(Runner.jsCommand(outputPath.toIO, args, jsDom = false, emitWasm = true)) + } + else { + val process = value { runtime match { case WasmRuntime.Deno => - Left(Runner.denoCommand(outputPath.toIO, args, denoPathOpt = denoPathOpt)) - case _ => - Left(Runner.jsCommand(outputPath.toIO, args, jsDom = false, emitWasm = true)) - } - else { - val process = value { - runtime match { - case WasmRuntime.Deno => - Runner.runDeno( - outputPath.toIO, - args, - logger, - allowExecve = effectiveAllowExecve, - emitWasm = true, - denoPathOpt = denoPathOpt - ) - case _ => - Runner.runJs( - outputPath.toIO, - args, - logger, - allowExecve = effectiveAllowExecve, - jsDom = false, - sourceMap = build.options.scalaJsOptions.emitSourceMaps, - esModule = esModule, - emitWasm = true - ) - } + Runner.runDeno( + outputPath.toIO, + args, + logger, + allowExecve = effectiveAllowExecve, + emitWasm = true + ) + case WasmRuntime.Node => + Runner.runJs( + outputPath.toIO, + args, + logger, + allowExecve = effectiveAllowExecve, + jsDom = false, + sourceMap = build.options.scalaJsOptions.emitSourceMaps, + esModule = esModule, + emitWasm = true + ) } - process.onExit().thenApply(_ => if os.exists(jsDest) then os.remove(jsDest)) - Right((process, None)) } + process.onExit().thenApply(_ => if os.exists(jsDest) then os.remove(jsDest)) + Right((process, None)) } - value(res) - } - else { - // Standalone WASM runtimes - not yet supported. - // Scala.js currently produces JS-dependent WASM output. - // Standalone support requires upstream Scala.js changes (scala-js/scala-js#4991). - val runtimeName = runtime.name - val extraNote = runtime match { - case WasmRuntime.Wasmer => - " Note: Wasmer does not yet support WasmGC, which is required for Scala WASM output." - case _ => "" - } - value(Left(new UnsupportedWasmRuntimeError( - s"Standalone WASM runtime '$runtimeName' is not yet supported." + - s"$extraNote" + - " Scala.js currently produces JavaScript-dependent WASM output." + - " Standalone WASM support is tracked at: https://github.com/scala-js/scala-js/issues/4991" + - " Use --wasm-runtime node (default) or --wasm-runtime deno for JS-based WASM execution." - ))) } + value(res) } else build.options.platform.value match { diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/HelpGroupOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/HelpGroupOptions.scala index ef012e22f0..76d78dcb19 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/HelpGroupOptions.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/HelpGroupOptions.scala @@ -49,7 +49,13 @@ case class HelpGroupOptions( @Name("fmtHelp") @Tag(tags.implementation) @Tag(tags.inShortHelp) - helpScalafmt: Boolean = false + helpScalafmt: Boolean = false, + @Group(HelpGroup.Help.toString) + @HelpMessage("Show options for WebAssembly") + @Name("wasmHelp") + @Tag(tags.implementation) + @Tag(tags.inShortHelp) + helpWasm: Boolean = false ) { private def printHelpWithGroup(help: Help[?], helpFormat: HelpFormat, group: String): Nothing = { @@ -68,6 +74,7 @@ case class HelpGroupOptions( def maybePrintGroupHelp(help: Help[?], helpFormat: HelpFormat): Unit = { if (helpJs) printHelpWithGroup(help, helpFormat, HelpGroup.ScalaJs.toString) else if (helpNative) printHelpWithGroup(help, helpFormat, HelpGroup.ScalaNative.toString) + else if (helpWasm) printHelpWithGroup(help, helpFormat, HelpGroup.Wasm.toString) } } diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala index c48dbc43bd..9c988b6dca 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala @@ -313,13 +313,23 @@ final case class SharedOptions( ) } - private def buildWasmOptions(opts: WasmOptions): options.WasmOptions = { + private def buildWasmOptions( + opts: WasmOptions + ): Either[BuildException, options.WasmOptions] = { import opts._ - options.WasmOptions( - enabled = wasm, - runtime = - wasmRuntime.flatMap(options.WasmRuntime.parse).getOrElse(options.WasmRuntime.default), - denoVersion = denoVersion + val wasmEnabled = wasm || wasmRuntime.isDefined + val parsedRuntime: Either[BuildException, options.WasmRuntime] = + wasmRuntime.fold(Right(options.WasmRuntime.default)) { rt => + options.WasmRuntime.parse(rt).toRight { + val validValues = options.WasmRuntime.all.map(_.name).mkString(", ") + new scala.build.errors.UnrecognizedWasmRuntimeError(rt, validValues) + } + } + parsedRuntime.map(runtime => + options.WasmOptions( + enabled = wasmEnabled, + runtime = runtime + ) ) } @@ -348,7 +358,7 @@ final case class SharedOptions( } val parsedPlatform = platform.map(Platform.normalize).flatMap(Platform.parse) // WASM mode requires Scala.js platform for compilation - val wasmEnabled = wasmOptions.wasm + val wasmEnabled = wasmOptions.wasm || wasmOptions.wasmRuntime.isDefined val platformOpt = value { (parsedPlatform, js.js, native.native, wasmEnabled) match { case (Some(p: Platform.JS.type), _, false, _) => Right(Some(p)) @@ -454,7 +464,7 @@ final case class SharedOptions( ), scalaJsOptions = scalaJsOptions(js), scalaNativeOptions = snOpts, - wasmOptions = buildWasmOptions(wasmOptions), + wasmOptions = value(buildWasmOptions(wasmOptions)), javaOptions = value(scala.cli.commands.util.JvmUtils.javaOptions(jvm)), jmhOptions = scala.build.options.JmhOptions( jmhVersion = benchmarking.jmhVersion, diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala index d8793afaa2..a2e6251fdc 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala @@ -8,20 +8,15 @@ import scala.cli.commands.tags // format: off final case class WasmOptions( - @Group(HelpGroup.Scala.toString) + @Group(HelpGroup.Wasm.toString) @Tag(tags.experimental) @HelpMessage("Enable WebAssembly output (Scala.js WASM backend). Uses Node.js by default. To show more options for WASM pass `--help-wasm`") wasm: Boolean = false, @Group(HelpGroup.Wasm.toString) @Tag(tags.experimental) - @HelpMessage("WASM runtime to use: node (default), deno. Standalone runtimes (wasmtime, wasmedge) planned for future releases.") - wasmRuntime: Option[String] = None, - - @Group(HelpGroup.Wasm.toString) - @Tag(tags.experimental) - @HelpMessage("Version of Deno to use. If Deno is not found on PATH, it will be downloaded automatically.") - denoVersion: Option[String] = None + @HelpMessage("WASM runtime to use: node (default), deno") + wasmRuntime: Option[String] = None ) // format: on diff --git a/modules/cli/src/main/scala/scala/cli/internal/WasmRuntimeDownloader.scala b/modules/cli/src/main/scala/scala/cli/internal/WasmRuntimeDownloader.scala deleted file mode 100644 index 751e087034..0000000000 --- a/modules/cli/src/main/scala/scala/cli/internal/WasmRuntimeDownloader.scala +++ /dev/null @@ -1,104 +0,0 @@ -package scala.cli.internal - -import coursier.cache.ArchiveCache -import coursier.util.Task - -import java.util.Locale - -import scala.build.EitherCps.{either, value} -import scala.build.Logger -import scala.build.errors.BuildException -import scala.build.internal.FetchExternalBinary -import scala.util.Properties - -/** Resolves Deno binary for WASM execution. - * - * Deno is first looked up on the system PATH. If not found, it is downloaded from GitHub releases - * and cached via Coursier's ArchiveCache. - */ -object WasmRuntimeDownloader { - - /** Returns the command to run Deno. - * - * First checks system PATH, otherwise downloads the binary. - */ - def denoCommand( - version: String, - archiveCache: ArchiveCache[Task], - logger: Logger - ): Either[BuildException, Seq[String]] = either { - findOnPath("deno") match { - case Some(path) => - logger.debug(s"Using system deno at: $path") - Seq(path) - case None => - logger.message(s"Deno not found on PATH, downloading v$version...") - val binary = value(fetchDeno(version, archiveCache, logger)) - Seq(binary.toString) - } - } - - /** Find an executable on the system PATH */ - private def findOnPath(name: String): Option[String] = { - val exeName = if (Properties.isWin) s"$name.exe" else name - sys.env.get("PATH").flatMap { pathEnv => - pathEnv.split(java.io.File.pathSeparator).view.map { dir => - val file = new java.io.File(dir, exeName) - if (file.exists() && file.canExecute) Some(file.getAbsolutePath) - else None - }.find(_.isDefined).flatten - } - } - - private def detectOs(win: String, linux: String, mac: String): Either[BuildException, String] = - if (Properties.isWin) Right(win) - else if (Properties.isLinux) Right(linux) - else if (Properties.isMac) Right(mac) - else Left(new WasmRuntimeDownloadError(s"Unsupported OS: ${sys.props("os.name")}")) - - private def detectArch64(x86_64: String, aarch64: String): Either[BuildException, String] = - sys.props("os.arch").toLowerCase(Locale.ROOT) match { - case "amd64" | "x86_64" => Right(x86_64) - case "aarch64" | "arm64" => Right(aarch64) - case other => Left(new WasmRuntimeDownloadError(s"Unsupported architecture: $other")) - } - - /** Fetches Deno binary for the current platform. - * - * Deno releases are at: - * https://github.com/denoland/deno/releases/download/v{version}/deno-{platform}.zip - */ - private def fetchDeno( - version: String, - archiveCache: ArchiveCache[Task], - logger: Logger - ): Either[BuildException, os.Path] = either { - val platform = value(denoPlatform) - val url = s"https://github.com/denoland/deno/releases/download/v$version/deno-$platform.zip" - - val binaryOpt = value { - FetchExternalBinary.fetchLauncher( - url = url, - changing = false, - archiveCache = archiveCache, - logger = logger, - launcherPrefix = "deno", - launcherPathOpt = None, - makeExecutable = true - ) - } - - binaryOpt.getOrElse { - value(Left(new WasmRuntimeDownloadError(s"Could not download Deno v$version for $platform"))) - } - } - - /** Platform suffix for Deno downloads */ - private def denoPlatform: Either[BuildException, String] = either { - val arch = value(detectArch64("x86_64", "aarch64")) - val os = value(detectOs("pc-windows-msvc", "unknown-linux-gnu", "apple-darwin")) - s"$arch-$os" - } -} - -class WasmRuntimeDownloadError(message: String) extends BuildException(message) diff --git a/modules/core/src/main/scala/scala/build/errors/UnrecognizedWasmRuntimeError.scala b/modules/core/src/main/scala/scala/build/errors/UnrecognizedWasmRuntimeError.scala new file mode 100644 index 0000000000..46e2f43b6c --- /dev/null +++ b/modules/core/src/main/scala/scala/build/errors/UnrecognizedWasmRuntimeError.scala @@ -0,0 +1,4 @@ +package scala.build.errors + +class UnrecognizedWasmRuntimeError(runtime: String, validValues: String) + extends BuildException(s"Unrecognized WASM runtime: '$runtime'. Valid values: $validValues") diff --git a/modules/core/src/main/scala/scala/build/errors/UnsupportedWasmRuntimeError.scala b/modules/core/src/main/scala/scala/build/errors/UnsupportedWasmRuntimeError.scala deleted file mode 100644 index b663b6a956..0000000000 --- a/modules/core/src/main/scala/scala/build/errors/UnsupportedWasmRuntimeError.scala +++ /dev/null @@ -1,3 +0,0 @@ -package scala.build.errors - -final class UnsupportedWasmRuntimeError(message: String) extends BuildException(message) diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala index 361fcc32ab..9a1b7f67f9 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala @@ -2,7 +2,7 @@ package scala.build.preprocessing.directives import scala.build.Positioned import scala.build.directives.* -import scala.build.errors.BuildException +import scala.build.errors.{BuildException, UnrecognizedWasmRuntimeError} import scala.build.options.{BuildOptions, Platform, ScalaOptions, WasmOptions, WasmRuntime} import scala.cli.commands.SpecificationLevel @@ -10,40 +10,44 @@ import scala.cli.commands.SpecificationLevel @DirectiveExamples("//> using wasm") @DirectiveExamples("//> using wasmRuntime node") @DirectiveExamples("//> using wasmRuntime deno") -@DirectiveExamples("//> using denoVersion 2.1.4") @DirectiveUsage( - "//> using wasm|wasmRuntime|denoVersion _value_", + "//> using wasm|wasmRuntime _value_", """ |`//> using wasm` _true|false_ | |`//> using wasm` | - |`//> using wasmRuntime` _node|deno|wasmtime|wasmedge|wasmer_ - | - |`//> using denoVersion` _value_ + |`//> using wasmRuntime` _node|deno_ |""".stripMargin ) @DirectiveDescription("Add WebAssembly options") @DirectiveLevel(SpecificationLevel.EXPERIMENTAL) final case class Wasm( wasm: Option[Boolean] = None, - wasmRuntime: Option[String] = None, - denoVersion: Option[String] = None + wasmRuntime: Option[String] = None ) extends HasBuildOptions { def buildOptions: Either[BuildException, BuildOptions] = { - val parsedRuntime = wasmRuntime.flatMap(WasmRuntime.parse) - val wasmOptions = WasmOptions( - enabled = wasm.getOrElse(false), - runtime = parsedRuntime.getOrElse(WasmRuntime.default), - denoVersion = denoVersion - ) - // When WASM is enabled, force Platform.JS (Scala.js WASM backend requires JS compilation) - val scalaOptions = - if (wasm.getOrElse(false)) - ScalaOptions(platform = Some(Positioned.none(Platform.JS))) - else - ScalaOptions() - Right(BuildOptions(scalaOptions = scalaOptions, wasmOptions = wasmOptions)) + val parsedRuntime = + wasmRuntime.fold(Right(WasmRuntime.default): Either[BuildException, WasmRuntime]) { rt => + WasmRuntime.parse(rt).toRight { + val validValues = WasmRuntime.all.map(_.name).mkString(", ") + new UnrecognizedWasmRuntimeError(rt, validValues) + } + } + parsedRuntime.map { runtime => + val wasmEnabled = wasm.getOrElse(false) || wasmRuntime.isDefined + val wasmOptions = WasmOptions( + enabled = wasmEnabled, + runtime = runtime + ) + // When WASM is enabled, force Platform.JS (Scala.js WASM backend requires JS compilation) + val scalaOptions = + if (wasmEnabled) + ScalaOptions(platform = Some(Positioned.none(Platform.JS))) + else + ScalaOptions() + BuildOptions(scalaOptions = scalaOptions, wasmOptions = wasmOptions) + } } } diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala index 8436bc2e0f..7ff3972f7a 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala @@ -428,32 +428,6 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => } } - for (runtime <- Seq("wasmtime", "wasmedge", "wasmer")) - test(s"Unsupported WASM runtime '$runtime' gives clear error") { - val inputs = TestInputs( - os.rel / "Hello.scala" -> - """object Hello { - | def main(args: Array[String]): Unit = println("Hello!") - |} - |""".stripMargin - ) - inputs.fromRoot { root => - val res = os.proc( - TestUtil.cli, - "--power", - "run", - "Hello.scala", - "--wasm", - "--wasm-runtime", - runtime, - extraOptions - ).call(cwd = root, check = false, mergeErrIntoOut = true) - expect(res.exitCode != 0) - expect(res.out.trim().contains("not yet supported")) - expect(res.out.trim().contains("scala-js/scala-js/issues/4991")) - } - } - if (TestUtil.fromPath("deno").isDefined) test("Run with --wasm-runtime deno") { val inputs = TestInputs( diff --git a/modules/options/src/main/scala/scala/build/options/WasmOptions.scala b/modules/options/src/main/scala/scala/build/options/WasmOptions.scala index f96d697803..34450e2385 100644 --- a/modules/options/src/main/scala/scala/build/options/WasmOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/WasmOptions.scala @@ -1,24 +1,16 @@ package scala.build.options -import scala.build.internal.Constants - /** Options for WebAssembly compilation and execution. * * @param enabled * If true, enable WASM output (Scala.js WASM backend) * @param runtime - * The WASM runtime to use for execution (node, deno, wasmtime, wasmedge, wasmer) - * @param denoVersion - * Version of Deno to download (if not found on PATH) + * The WASM runtime to use for execution (node, deno) */ final case class WasmOptions( enabled: Boolean = false, - runtime: WasmRuntime = WasmRuntime.default, - denoVersion: Option[String] = None -) { - def finalDenoVersion: String = - denoVersion.filter(_.nonEmpty).getOrElse(Constants.defaultDenoVersion) -} + runtime: WasmRuntime = WasmRuntime.default +) object WasmOptions { implicit val hasHashData: HasHashData[WasmOptions] = HasHashData.derive diff --git a/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala b/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala index f88f3028ab..a2e68d63f8 100644 --- a/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala +++ b/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala @@ -7,30 +7,14 @@ import java.util.Locale * JS-based runtimes (work now with Scala.js WASM backend): * - Node: Uses Node.js (V8 engine) with JavaScript loader * - Deno: Uses Deno (V8 engine) with ES module support - * - * Standalone runtimes (future, requires upstream Scala.js standalone WASM support): - * - Wasmtime: Primary standalone target, full WasmGC + Component Model - * - WasmEdge: Secondary standalone target, CNCF cloud-native runtime - * - Wasmer: Placeholder, no WasmGC support yet */ -sealed abstract class WasmRuntime(val name: String) { - def isJsBased: Boolean = this match { - case WasmRuntime.Node | WasmRuntime.Deno => true - case _ => false - } - def isStandalone: Boolean = !isJsBased -} +sealed abstract class WasmRuntime(val name: String) object WasmRuntime { - // JS-based runtimes (work now) case object Node extends WasmRuntime("node") case object Deno extends WasmRuntime("deno") - // Standalone runtimes (future - requires upstream Scala.js standalone WASM support) - case object Wasmtime extends WasmRuntime("wasmtime") - case object WasmEdge extends WasmRuntime("wasmedge") - case object Wasmer extends WasmRuntime("wasmer") - val all: Seq[WasmRuntime] = Seq(Node, Deno, Wasmtime, WasmEdge, Wasmer) + val all: Seq[WasmRuntime] = Seq(Node, Deno) def default: WasmRuntime = Node @@ -38,9 +22,6 @@ object WasmRuntime { s.trim.toLowerCase(Locale.ROOT) match { case "node" | "nodejs" => Some(Node) case "deno" => Some(Deno) - case "wasmtime" => Some(Wasmtime) - case "wasmedge" => Some(WasmEdge) - case "wasmer" => Some(Wasmer) case _ => None } diff --git a/website/docs/reference/cli-options.md b/website/docs/reference/cli-options.md index fff9d3918f..fa46d5181f 100644 --- a/website/docs/reference/cli-options.md +++ b/website/docs/reference/cli-options.md @@ -591,6 +591,12 @@ Aliases: `--fmt-help`, `--help-fmt`, `--scalafmt-help` Show options for Scalafmt +### `--help-wasm` + +Aliases: `--wasm-help` + +Show options for WebAssembly + ## Install completions options Available in commands: @@ -1966,31 +1972,21 @@ A github token used to access GitHub. Not needed in most cases. Don't check for the newest available Scala CLI version upstream -## WebAssembly options +## Wasm options Available in commands: -[`run`](./commands.md#run), [`shebang`](./commands.md#shebang) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) ### `--wasm` -[Experimental] - Enable WebAssembly output (Scala.js WASM backend). Uses Node.js by default. To show more options for WASM pass `--help-wasm` ### `--wasm-runtime` -[Experimental] - -WASM runtime to use: node (default), deno. Standalone runtimes (wasmtime, wasmedge) planned for future releases. - -### `--deno-version` - -[Experimental] - -Version of Deno to use. If Deno is not found on PATH, it will be downloaded automatically. +WASM runtime to use: node (default), deno ## Watch options diff --git a/website/docs/reference/commands.md b/website/docs/reference/commands.md index 9aaeff82ed..643f0cd72e 100644 --- a/website/docs/reference/commands.md +++ b/website/docs/reference/commands.md @@ -32,7 +32,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/compile -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [compile](./cli-options.md#compile-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [compile](./cli-options.md#compile-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## config @@ -83,7 +83,7 @@ Accepts option groups: [config](./cli-options.md#config-options), [coursier](./c Update dependency directives in the project -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [dependency update](./cli-options.md#dependency-update-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [dependency update](./cli-options.md#dependency-update-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) ## doc @@ -97,7 +97,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/doc -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [doc](./cli-options.md#doc-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [doc](./cli-options.md#doc-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) ## export @@ -119,7 +119,7 @@ The `export` sub-command is experimental. Please bear in mind that non-ideal user experience should be expected. If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [export](./cli-options.md#export-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [export](./cli-options.md#export-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) ## fix @@ -145,7 +145,7 @@ The `fix` sub-command is experimental. Please bear in mind that non-ideal user experience should be expected. If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fix](./cli-options.md#fix-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [Scalafix](./cli-options.md#scalafix-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fix](./cli-options.md#fix-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [Scalafix](./cli-options.md#scalafix-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) ## fmt @@ -162,7 +162,7 @@ All standard Scala CLI inputs are accepted, but only Scala sources will be forma For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/fmt -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fmt](./cli-options.md#fmt-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fmt](./cli-options.md#fmt-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) ## help @@ -214,7 +214,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/repl -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [repl](./cli-options.md#repl-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [repl](./cli-options.md#repl-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## package @@ -232,7 +232,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/package -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [package](./cli-options.md#package-options), [packager](./cli-options.md#packager-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [package](./cli-options.md#package-options), [packager](./cli-options.md#packager-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## publish @@ -259,7 +259,7 @@ The `publish` sub-command is experimental. Please bear in mind that non-ideal user experience should be expected. If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [publish](./cli-options.md#publish-options), [publish connection](./cli-options.md#publish-connection-options), [publish params](./cli-options.md#publish-params-options), [publish repository](./cli-options.md#publish-repository-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [publish](./cli-options.md#publish-options), [publish connection](./cli-options.md#publish-connection-options), [publish params](./cli-options.md#publish-params-options), [publish repository](./cli-options.md#publish-repository-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## publish local @@ -271,7 +271,7 @@ The `publish-local` sub-command is experimental. Please bear in mind that non-ideal user experience should be expected. If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [publish](./cli-options.md#publish-options), [publish local](./cli-options.md#publish-local-options), [publish params](./cli-options.md#publish-params-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [publish](./cli-options.md#publish-options), [publish local](./cli-options.md#publish-local-options), [publish params](./cli-options.md#publish-params-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## publish setup @@ -309,7 +309,7 @@ To pass arguments to the actual application, just add them after `--`, like: For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/run -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## github secret create @@ -356,7 +356,7 @@ Using directives can be defined in all supported input source file types. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/setup-ide -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp file](./cli-options.md#bsp-file-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [setup IDE](./cli-options.md#setup-ide-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp file](./cli-options.md#bsp-file-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [setup IDE](./cli-options.md#setup-ide-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) ## shebang @@ -387,7 +387,7 @@ Using this, it is possible to conveniently set up Unix shebang scripts. For exam For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/shebang -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## test @@ -411,7 +411,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/test -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [test](./cli-options.md#test-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [test](./cli-options.md#test-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## uninstall @@ -514,7 +514,7 @@ It is normally supposed to be invoked by your IDE when a Scala CLI project is im Detailed documentation can be found on our website: https://scala-cli.virtuslab.org -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp](./cli-options.md#bsp-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp](./cli-options.md#bsp-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) ### default-file diff --git a/website/docs/reference/directives.md b/website/docs/reference/directives.md index 0e1894dde5..cf75f0cbaf 100644 --- a/website/docs/reference/directives.md +++ b/website/docs/reference/directives.md @@ -680,27 +680,6 @@ Add Scala.js options `//> using jsEmitWasm` -### WebAssembly - -Add WebAssembly options - -`//> using wasm` _true|false_ - -`//> using wasm` - -`//> using wasmRuntime` _node|deno|wasmtime|wasmedge|wasmer_ - -`//> using denoVersion` _value_ - -#### Examples -`//> using wasm` - -`//> using wasmRuntime node` - -`//> using wasmRuntime deno` - -`//> using denoVersion 2.1.4` - ### Test framework Set the test framework @@ -728,6 +707,25 @@ Use a toolkit as dependency (not supported in Scala 2.12), 'default' version for `//> using test.toolkit default` +### WASM options + +Add WebAssembly options + + +`//> using wasm` _true|false_ + +`//> using wasm` + +`//> using wasmRuntime` _node|deno_ + + +#### Examples +`//> using wasm` + +`//> using wasmRuntime node` + +`//> using wasmRuntime deno` + ### Watch additional inputs Watch additional files or directories when using watch mode diff --git a/website/docs/reference/scala-command/cli-options.md b/website/docs/reference/scala-command/cli-options.md index 74cba5642f..bd8ebaf16a 100644 --- a/website/docs/reference/scala-command/cli-options.md +++ b/website/docs/reference/scala-command/cli-options.md @@ -498,6 +498,14 @@ Aliases: `--fmt-help`, `--help-fmt`, `--scalafmt-help` Show options for Scalafmt +### `--help-wasm` + +Aliases: `--wasm-help` + +`IMPLEMENTATION specific` per Scala Runner specification + +Show options for WebAssembly + ## Install completions options Available in commands: @@ -1487,6 +1495,16 @@ A github token used to access GitHub. Not needed in most cases. Don't check for the newest available Scala CLI version upstream +## Wasm options + +Available in commands: + +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) + + + +*This section was automatically generated and may be empty if no options were available.* + ## Watch options Available in commands: diff --git a/website/docs/reference/scala-command/commands.md b/website/docs/reference/scala-command/commands.md index 8b7c42a9eb..e7fbd71e5a 100644 --- a/website/docs/reference/scala-command/commands.md +++ b/website/docs/reference/scala-command/commands.md @@ -31,7 +31,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/compile -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [compile](./cli-options.md#compile-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [compile](./cli-options.md#compile-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ### config @@ -90,7 +90,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/doc -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [doc](./cli-options.md#doc-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [doc](./cli-options.md#doc-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) ### repl @@ -112,7 +112,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/repl -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [repl](./cli-options.md#repl-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [repl](./cli-options.md#repl-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ### run @@ -138,7 +138,7 @@ To pass arguments to the actual application, just add them after `--`, like: For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/run -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ### shebang @@ -169,7 +169,7 @@ Using this, it is possible to conveniently set up Unix shebang scripts. For exam For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/shebang -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## SHOULD have commands: @@ -188,7 +188,7 @@ All standard Scala CLI inputs are accepted, but only Scala sources will be forma For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/fmt -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fmt](./cli-options.md#fmt-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fmt](./cli-options.md#fmt-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) ### test @@ -212,7 +212,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/test -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [test](./cli-options.md#test-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [test](./cli-options.md#test-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ### version @@ -244,7 +244,7 @@ It is normally supposed to be invoked by your IDE when a Scala CLI project is im Detailed documentation can be found on our website: https://scala-cli.virtuslab.org -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp](./cli-options.md#bsp-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp](./cli-options.md#bsp-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) ### clean @@ -296,7 +296,7 @@ Using directives can be defined in all supported input source file types. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/setup-ide -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp file](./cli-options.md#bsp-file-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [setup IDE](./cli-options.md#setup-ide-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp file](./cli-options.md#bsp-file-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [setup IDE](./cli-options.md#setup-ide-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) ### uninstall diff --git a/website/docs/reference/scala-command/runner-specification.md b/website/docs/reference/scala-command/runner-specification.md index d0a3a55050..4be6903a23 100644 --- a/website/docs/reference/scala-command/runner-specification.md +++ b/website/docs/reference/scala-command/runner-specification.md @@ -636,6 +636,12 @@ Show options for Scalafmt Aliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help` +**--help-wasm** + +Show options for WebAssembly + +Aliases: `--wasm-help` + **--strict-bloop-json-check** @@ -1445,6 +1451,12 @@ Show options for Scalafmt Aliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help` +**--help-wasm** + +Show options for WebAssembly + +Aliases: `--wasm-help` + **--strict-bloop-json-check** @@ -2070,6 +2082,12 @@ Show options for Scalafmt Aliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help` +**--help-wasm** + +Show options for WebAssembly + +Aliases: `--wasm-help` + **--strict-bloop-json-check** @@ -2735,6 +2753,12 @@ Show options for Scalafmt Aliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help` +**--help-wasm** + +Show options for WebAssembly + +Aliases: `--wasm-help` + **--strict-bloop-json-check** @@ -3399,6 +3423,12 @@ Show options for Scalafmt Aliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help` +**--help-wasm** + +Show options for WebAssembly + +Aliases: `--wasm-help` + **--strict-bloop-json-check** @@ -4021,6 +4051,12 @@ Show options for Scalafmt Aliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help` +**--help-wasm** + +Show options for WebAssembly + +Aliases: `--wasm-help` + **--strict-bloop-json-check** @@ -4721,6 +4757,12 @@ Show options for Scalafmt Aliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help` +**--help-wasm** + +Show options for WebAssembly + +Aliases: `--wasm-help` + **--strict-bloop-json-check** @@ -5431,6 +5473,12 @@ Show options for Scalafmt Aliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help` +**--help-wasm** + +Show options for WebAssembly + +Aliases: `--wasm-help` + **--strict-bloop-json-check** @@ -6424,6 +6472,12 @@ Show options for Scalafmt Aliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help` +**--help-wasm** + +Show options for WebAssembly + +Aliases: `--wasm-help` + **--strict-bloop-json-check** From eb7a7661501b0ddf3a52dae205ee2fd073899bad Mon Sep 17 00:00:00 2001 From: lostflydev Date: Sun, 19 Apr 2026 12:57:10 +0500 Subject: [PATCH 3/9] - Add Bun as a third WASM runtime (--wasm-runtime bun / //> using wasmRuntime bun) - Add BunNotFoundError with install hint - Add integration test for Bun (conditional on bun being on PATH) - Add actions/setup-node@v6 node-version:24 to all Linux integration test jobs: the default Node.js on ubuntu-24.04 runners is too old for Scala.js WASM GC (which requires Node.js >= 22). Matches docs-tests job which already pins node-version: 24 --- .github/workflows/ci.yml | 51 +++++++++++++++++++ .../scala/scala/build/internal/Runner.scala | 49 ++++++++++++++++++ .../scala/scala/cli/commands/run/Run.scala | 9 ++++ .../cli/commands/shared/WasmOptions.scala | 2 +- .../scala/build/errors/BunNotFoundError.scala | 5 ++ .../build/preprocessing/directives/Wasm.scala | 3 +- .../RunScalaJsTestDefinitions.scala | 24 +++++++++ .../scala/build/options/WasmRuntime.scala | 5 +- website/docs/reference/cli-options.md | 2 +- website/docs/reference/directives.md | 4 +- 10 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 modules/core/src/main/scala/scala/build/errors/BunNotFoundError.scala diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3446258058..578a2acbe5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -158,6 +158,9 @@ jobs: if: env.SHOULD_RUN == 'true' with: jvm: "temurin:17" + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: JVM integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.jvmBootstrapped @@ -196,6 +199,9 @@ jobs: if: env.SHOULD_RUN == 'true' with: jvm: "temurin:17" + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: JVM integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.jvm @@ -234,6 +240,9 @@ jobs: if: env.SHOULD_RUN == 'true' with: jvm: "temurin:17" + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: JVM integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.jvm @@ -272,6 +281,9 @@ jobs: if: env.SHOULD_RUN == 'true' with: jvm: "temurin:17" + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: JVM integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.jvm @@ -310,6 +322,9 @@ jobs: if: env.SHOULD_RUN == 'true' with: jvm: "temurin:17" + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: JVM integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.jvm @@ -348,6 +363,9 @@ jobs: if: env.SHOULD_RUN == 'true' with: jvm: "temurin:17" + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: JVM integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.jvm @@ -436,6 +454,9 @@ jobs: with: name: linux-launchers path: artifacts/ + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: Native integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i nativeIntegrationTests @@ -482,6 +503,9 @@ jobs: with: name: linux-launchers path: artifacts/ + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: Native integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i nativeIntegrationTests @@ -528,6 +552,9 @@ jobs: with: name: linux-launchers path: artifacts/ + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: Native integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i nativeIntegrationTests @@ -574,6 +601,9 @@ jobs: with: name: linux-launchers path: artifacts/ + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: Native integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i nativeIntegrationTests @@ -620,6 +650,9 @@ jobs: with: name: linux-launchers path: artifacts/ + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: Native integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i nativeIntegrationTests @@ -713,6 +746,9 @@ jobs: with: name: linux-aarch64-launchers path: artifacts/ + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: Native integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i nativeIntegrationTests @@ -759,6 +795,9 @@ jobs: with: name: linux-aarch64-launchers path: artifacts/ + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: Native integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i nativeIntegrationTests @@ -1417,6 +1456,9 @@ jobs: - name: Build slim docker image if: env.SHOULD_RUN == 'true' run: .github/scripts/generate-slim-docker-image.sh + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: Native integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.nativeMostlyStatic @@ -1475,6 +1517,9 @@ jobs: with: name: mostly-static-launchers path: artifacts/ + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: Native integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.nativeMostlyStatic @@ -1563,6 +1608,9 @@ jobs: - name: Build docker image if: env.SHOULD_RUN == 'true' run: .github/scripts/generate-docker-image.sh + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: Native integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.nativeStatic @@ -1624,6 +1672,9 @@ jobs: - name: Build docker image if: env.SHOULD_RUN == 'true' run: .github/scripts/generate-docker-image.sh + - uses: actions/setup-node@v6 + with: + node-version: 24 - name: Native integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.nativeStatic diff --git a/modules/build/src/main/scala/scala/build/internal/Runner.scala b/modules/build/src/main/scala/scala/build/internal/Runner.scala index dc8e274fa6..9fee67a7d0 100644 --- a/modules/build/src/main/scala/scala/build/internal/Runner.scala +++ b/modules/build/src/main/scala/scala/build/internal/Runner.scala @@ -435,6 +435,55 @@ object Runner { } } + def bunCommand( + entrypoint: File, + args: Seq[String] + ): Seq[String] = { + val bunPath = findInPath("bun").fold("bun")(_.toString) + Seq(bunPath, "run", entrypoint.getAbsolutePath) ++ args + } + + def runBun( + entrypoint: File, + args: Seq[String], + logger: Logger, + allowExecve: Boolean = false + ): Either[BuildException, Process] = either { + val bunPath: String = + value(findInPath("bun") + .map(_.toString) + .toRight(BunNotFoundError())) + + val command = Seq(bunPath, "run", entrypoint.getAbsolutePath) ++ args + + if (allowExecve && Execve.available()) { + logger.log( + s"Running ${command.mkString(" ")}", + " Running" + System.lineSeparator() + + command.iterator.map(_ + System.lineSeparator()).mkString + ) + + logger.debug("execve available") + Execve.execve( + command.head, + "bun" +: command.tail.toArray, + sys.env.toArray.sorted.map { case (k, v) => s"$k=$v" } + ) + sys.error("should not happen") + } + else { + logger.log( + s"Running ${command.mkString(" ")}", + " Running" + System.lineSeparator() + + command.iterator.map(_ + System.lineSeparator()).mkString + ) + + new ProcessBuilder(command*) + .inheritIO() + .start() + } + } + def runNative( launcher: File, args: Seq[String], diff --git a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala index c8253ee14e..e7a4be4161 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala @@ -508,6 +508,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { Left(Runner.denoCommand(outputPath.toIO, args)) case WasmRuntime.Node => Left(Runner.jsCommand(outputPath.toIO, args, jsDom = false, emitWasm = true)) + case WasmRuntime.Bun => + Left(Runner.bunCommand(outputPath.toIO, args)) } else { val process = value { @@ -531,6 +533,13 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { esModule = esModule, emitWasm = true ) + case WasmRuntime.Bun => + Runner.runBun( + outputPath.toIO, + args, + logger, + allowExecve = effectiveAllowExecve + ) } } process.onExit().thenApply(_ => if os.exists(jsDest) then os.remove(jsDest)) diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala index a2e6251fdc..da99420d95 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala @@ -15,7 +15,7 @@ final case class WasmOptions( @Group(HelpGroup.Wasm.toString) @Tag(tags.experimental) - @HelpMessage("WASM runtime to use: node (default), deno") + @HelpMessage("WASM runtime to use: node (default), deno, bun") wasmRuntime: Option[String] = None ) // format: on diff --git a/modules/core/src/main/scala/scala/build/errors/BunNotFoundError.scala b/modules/core/src/main/scala/scala/build/errors/BunNotFoundError.scala new file mode 100644 index 0000000000..b6665fef28 --- /dev/null +++ b/modules/core/src/main/scala/scala/build/errors/BunNotFoundError.scala @@ -0,0 +1,5 @@ +package scala.build.errors + +final class BunNotFoundError extends BuildException( + "Bun was not found on the PATH. Install Bun from https://bun.sh/ or use --wasm-runtime node" + ) \ No newline at end of file diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala index 9a1b7f67f9..d8c959f6fb 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala @@ -10,6 +10,7 @@ import scala.cli.commands.SpecificationLevel @DirectiveExamples("//> using wasm") @DirectiveExamples("//> using wasmRuntime node") @DirectiveExamples("//> using wasmRuntime deno") +@DirectiveExamples("//> using wasmRuntime bun") @DirectiveUsage( "//> using wasm|wasmRuntime _value_", """ @@ -17,7 +18,7 @@ import scala.cli.commands.SpecificationLevel | |`//> using wasm` | - |`//> using wasmRuntime` _node|deno_ + |`//> using wasmRuntime` _node|deno|bun_ |""".stripMargin ) @DirectiveDescription("Add WebAssembly options") diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala index 7ff3972f7a..962091e74f 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala @@ -452,6 +452,30 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => } } + if (TestUtil.fromPath("bun").isDefined) + test("Run with --wasm-runtime bun") { + val inputs = TestInputs( + os.rel / "Hello.scala" -> + """object Hello { + | def main(args: Array[String]): Unit = println("Hello from Bun WASM!") + |} + |""".stripMargin + ) + inputs.fromRoot { root => + val output = os.proc( + TestUtil.cli, + "--power", + "run", + "Hello.scala", + "--wasm", + "--wasm-runtime", + "bun", + extraOptions + ).call(cwd = root).out.trim() + expect(output == "Hello from Bun WASM!") + } + } + test("WASM multiple source files") { val inputs = TestInputs( os.rel / "Greeter.scala" -> diff --git a/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala b/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala index a2e68d63f8..b5f8814e5d 100644 --- a/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala +++ b/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala @@ -7,14 +7,16 @@ import java.util.Locale * JS-based runtimes (work now with Scala.js WASM backend): * - Node: Uses Node.js (V8 engine) with JavaScript loader * - Deno: Uses Deno (V8 engine) with ES module support + * - Bun: Uses Bun (JavaScriptCore engine) with ES module support */ sealed abstract class WasmRuntime(val name: String) object WasmRuntime { case object Node extends WasmRuntime("node") case object Deno extends WasmRuntime("deno") + case object Bun extends WasmRuntime("bun") - val all: Seq[WasmRuntime] = Seq(Node, Deno) + val all: Seq[WasmRuntime] = Seq(Node, Deno, Bun) def default: WasmRuntime = Node @@ -22,6 +24,7 @@ object WasmRuntime { s.trim.toLowerCase(Locale.ROOT) match { case "node" | "nodejs" => Some(Node) case "deno" => Some(Deno) + case "bun" => Some(Bun) case _ => None } diff --git a/website/docs/reference/cli-options.md b/website/docs/reference/cli-options.md index fa46d5181f..1909961585 100644 --- a/website/docs/reference/cli-options.md +++ b/website/docs/reference/cli-options.md @@ -1986,7 +1986,7 @@ Enable WebAssembly output (Scala.js WASM backend). Uses Node.js by default. To s ### `--wasm-runtime` -WASM runtime to use: node (default), deno +WASM runtime to use: node (default), deno, bun ## Watch options diff --git a/website/docs/reference/directives.md b/website/docs/reference/directives.md index cf75f0cbaf..d9640d57bf 100644 --- a/website/docs/reference/directives.md +++ b/website/docs/reference/directives.md @@ -716,7 +716,7 @@ Add WebAssembly options `//> using wasm` -`//> using wasmRuntime` _node|deno_ +`//> using wasmRuntime` _node|deno|bun_ #### Examples @@ -726,6 +726,8 @@ Add WebAssembly options `//> using wasmRuntime deno` +`//> using wasmRuntime bun` + ### Watch additional inputs Watch additional files or directories when using watch mode From 58c65b614d1457773c74725ac9ebf2a494381ec6 Mon Sep 17 00:00:00 2001 From: lostflydev Date: Wed, 6 May 2026 11:54:26 +0300 Subject: [PATCH 4/9] Always pass --experimental-wasm-exnref when emitting WASM Node 24 still ships V8 12.x where wasm-exnref is gated behind --experimental-wasm-exnref; the flag only flips to default in V8 13.x (Node 25+). The previous nodeMajorVersion < 24 guard therefore left Node 24 (the version pinned in CI) without the flag, which made any Scala.js WASM code using exception bytecodes, runtime throws, JS interop or Scala 3 @main fail at runtime. Same reasoning applies to Deno (Deno 2.x = V8 12.x). Until V8 13.x is the default everywhere, just always set the flag, there is no any overhead --- .../scala/scala/build/internal/Runner.scala | 39 +++++++++++-------- .../scala/scala/cli/commands/run/Run.scala | 4 +- .../scala/build/errors/BunNotFoundError.scala | 2 +- .../errors/BunVersionTooOldForWasmError.scala | 7 ++++ .../NodeVersionTooOldForWasmError.scala | 7 ++++ 5 files changed, 40 insertions(+), 19 deletions(-) create mode 100644 modules/core/src/main/scala/scala/build/errors/BunVersionTooOldForWasmError.scala create mode 100644 modules/core/src/main/scala/scala/build/errors/NodeVersionTooOldForWasmError.scala diff --git a/modules/build/src/main/scala/scala/build/internal/Runner.scala b/modules/build/src/main/scala/scala/build/internal/Runner.scala index 9fee67a7d0..e87e7e7786 100644 --- a/modules/build/src/main/scala/scala/build/internal/Runner.scala +++ b/modules/build/src/main/scala/scala/build/internal/Runner.scala @@ -210,24 +210,26 @@ object Runner { case _: Exception => None } - // Node 24+ (V8 13+) has wasm-exnref enabled by default; older versions need --experimental-wasm-exnref. - private def nodeNeedsWasmFlag: Boolean = - nodeMajorVersion.forall(_ < 24) // true if unknown or < 24 - - // Detects the major version of Deno on PATH; cached for the JVM lifetime (lazy val). - // Returns None if deno is not found or version cannot be parsed. - private lazy val denoMajorVersion: Option[Int] = + // Pre-V8 13.x runtimes need --experimental-wasm-exnref for the Scala.js WASM exception model. + // V8 13.x ships in Node 25+ (Node 24 is still on V8 12.x where exnref is gated behind the flag), + // so until the default flips we always pass the flag when emitWasm is true. + private def nodeNeedsWasmFlag: Boolean = true + + // Deno 2.x bundles V8 12.x where wasm-exnref is gated behind a flag; symmetrical reasoning to Node. + // We always set DENO_V8_FLAGS=--experimental-wasm-exnref on emitWasm until V8 13.x lands in Deno. + private def denoNeedsWasmFlag: Boolean = true + + // Detects the major version of Bun on PATH; cached for the JVM lifetime (lazy val). + // Returns None if bun is not found or version cannot be parsed. + private lazy val bunMajorVersion: Option[Int] = try { - val process = new ProcessBuilder("deno", "--version") + val process = new ProcessBuilder("bun", "--version") .redirectErrorStream(true) .start() val output = new String(process.getInputStream.readAllBytes()).trim process.waitFor() - // Deno version format: "deno 2.1.0 (release, aarch64-apple-darwin)\nv8 13.x\ntypescript 5.x" - // Extract major from first line - val firstLine = output.linesIterator.nextOption().getOrElse("") - val versionStr = firstLine.stripPrefix("deno ").takeWhile(c => c.isDigit || c == '.') - versionStr.takeWhile(_.isDigit) match { + // Bun version format: "1.1.30" -> extract 1 + output.takeWhile(_.isDigit) match { case s if s.nonEmpty => Some(s.toInt) case _ => None } @@ -236,10 +238,6 @@ object Runner { case _: Exception => None } - // Deno 2.x+ bundles V8 13+ which has wasm-exnref enabled by default; no flag needed. - private def denoNeedsWasmFlag: Boolean = - denoMajorVersion.forall(_ < 2) // true if unknown or < 2 - private def endsWithCaseInsensitive(s: String, suffix: String): Boolean = s.length >= suffix.length && s.regionMatches(true, s.length - suffix.length, suffix, 0, suffix.length) @@ -302,6 +300,10 @@ object Runner { value(findInPath("node") .map(_.toString) .toRight(NodeNotFoundError())) + if (emitWasm) + nodeMajorVersion.foreach { v => + if (v < 22) value(Left(new NodeVersionTooOldForWasmError(v))) + } val nodeFlags = if (emitWasm && nodeNeedsWasmFlag) List("--experimental-wasm-exnref") else Nil if !jsDom && allowExecve && Execve.available() then { val command = Seq(nodePath) ++ nodeFlags ++ Seq(entrypoint.getAbsolutePath) ++ args @@ -453,6 +455,9 @@ object Runner { value(findInPath("bun") .map(_.toString) .toRight(BunNotFoundError())) + bunMajorVersion.foreach { v => + if (v < 1) value(Left(new BunVersionTooOldForWasmError(v))) + } val command = Seq(bunPath, "run", entrypoint.getAbsolutePath) ++ args diff --git a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala index e7a4be4161..4e04a3d798 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala @@ -552,7 +552,9 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { build.options.platform.value match { case Platform.JS => val esModule = - build.options.scalaJsOptions.moduleKindStr.exists(m => m == "es" || m == "esmodule") + build.options.scalaJsOptions.moduleKindStr.exists(m => + m == "es" || m == "esmodule" + ) val linkerConfig = build.options.scalaJsOptions.linkerConfig(logger) val jsDest = { diff --git a/modules/core/src/main/scala/scala/build/errors/BunNotFoundError.scala b/modules/core/src/main/scala/scala/build/errors/BunNotFoundError.scala index b6665fef28..d5db2865a6 100644 --- a/modules/core/src/main/scala/scala/build/errors/BunNotFoundError.scala +++ b/modules/core/src/main/scala/scala/build/errors/BunNotFoundError.scala @@ -2,4 +2,4 @@ package scala.build.errors final class BunNotFoundError extends BuildException( "Bun was not found on the PATH. Install Bun from https://bun.sh/ or use --wasm-runtime node" - ) \ No newline at end of file + ) diff --git a/modules/core/src/main/scala/scala/build/errors/BunVersionTooOldForWasmError.scala b/modules/core/src/main/scala/scala/build/errors/BunVersionTooOldForWasmError.scala new file mode 100644 index 0000000000..5386aa56a6 --- /dev/null +++ b/modules/core/src/main/scala/scala/build/errors/BunVersionTooOldForWasmError.scala @@ -0,0 +1,7 @@ +package scala.build.errors + +final class BunVersionTooOldForWasmError(found: Int) + extends BuildException( + s"Scala.js WASM backend requires Bun >= 1, but found Bun $found. " + + "Upgrade Bun (https://bun.sh/) or switch runtime via --wasm-runtime node|deno." + ) diff --git a/modules/core/src/main/scala/scala/build/errors/NodeVersionTooOldForWasmError.scala b/modules/core/src/main/scala/scala/build/errors/NodeVersionTooOldForWasmError.scala new file mode 100644 index 0000000000..d1a48562d9 --- /dev/null +++ b/modules/core/src/main/scala/scala/build/errors/NodeVersionTooOldForWasmError.scala @@ -0,0 +1,7 @@ +package scala.build.errors + +final class NodeVersionTooOldForWasmError(found: Int) + extends BuildException( + s"Scala.js WASM backend requires Node.js >= 22, but found Node.js $found. " + + "Upgrade Node (https://nodejs.org/) or switch runtime via --wasm-runtime deno|bun." + ) From f237bf2deba40d94d851f8ca7658bebe6231b1ff Mon Sep 17 00:00:00 2001 From: lostflydev Date: Thu, 14 May 2026 18:47:27 +0500 Subject: [PATCH 5/9] Fix review feedback: Wasm capitalization, version-aware node flags, refactor into ScalaJsOptions - Replace "WASM" with "Wasm" per WebAssembly spec: contraction, not acronym - Fix nodeNeedsWasmFlag to be version-aware: only pass --experimental-wasm-exnref for Node < 25 (V8 12.x); Node 25+ has it enabled by default, Node 26+ may remove it - Remove Node/Bun pre-flight version checks; let runtime fail naturally on old versions - Remove else-if-emitWasm branch (not needed) - Refactor WasmOptions into ScalaJsOptions: jsEmitWasm and wasmRuntime are now fields of ScalaJsOptions at both build and CLI layers; CLI flags are now --js-emit-wasm and --js-wasm-runtime under the Wasm help group; WasmOptions classes removed - linkerConfig() now forces ESModule when jsEmitWasm=true - Update all integration test CLI flags to --js-emit-wasm / --js-wasm-runtime --- .../scala/scala/build/internal/Runner.scala | 51 ++--------- .../scala/scala/cli/commands/run/Run.scala | 19 ++--- .../cli/commands/shared/ScalaJsOptions.scala | 9 +- .../cli/commands/shared/SharedOptions.scala | 85 +++++++++---------- .../cli/commands/shared/WasmOptions.scala | 27 ------ .../scala/build/errors/BunNotFoundError.scala | 2 +- .../errors/BunVersionTooOldForWasmError.scala | 7 -- .../build/errors/DenoNotFoundError.scala | 2 +- .../NodeVersionTooOldForWasmError.scala | 7 -- .../errors/UnrecognizedWasmRuntimeError.scala | 2 +- .../build/preprocessing/directives/Wasm.scala | 15 ++-- .../RunScalaJsTestDefinitions.scala | 52 ++++++------ .../scala/build/options/BuildOptions.scala | 1 - .../scala/build/options/ScalaJsOptions.scala | 6 +- .../scala/build/options/WasmOptions.scala | 18 ---- .../scala/build/options/WasmRuntime.scala | 2 +- website/docs/reference/cli-options.md | 22 ++--- website/docs/reference/commands.md | 30 +++---- website/docs/reference/directives.md | 2 +- .../reference/scala-command/cli-options.md | 10 --- .../docs/reference/scala-command/commands.md | 18 ++-- 21 files changed, 131 insertions(+), 256 deletions(-) delete mode 100644 modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala delete mode 100644 modules/core/src/main/scala/scala/build/errors/BunVersionTooOldForWasmError.scala delete mode 100644 modules/core/src/main/scala/scala/build/errors/NodeVersionTooOldForWasmError.scala delete mode 100644 modules/options/src/main/scala/scala/build/options/WasmOptions.scala diff --git a/modules/build/src/main/scala/scala/build/internal/Runner.scala b/modules/build/src/main/scala/scala/build/internal/Runner.scala index e87e7e7786..d5c4d7585c 100644 --- a/modules/build/src/main/scala/scala/build/internal/Runner.scala +++ b/modules/build/src/main/scala/scala/build/internal/Runner.scala @@ -210,34 +210,16 @@ object Runner { case _: Exception => None } - // Pre-V8 13.x runtimes need --experimental-wasm-exnref for the Scala.js WASM exception model. - // V8 13.x ships in Node 25+ (Node 24 is still on V8 12.x where exnref is gated behind the flag), - // so until the default flips we always pass the flag when emitWasm is true. - private def nodeNeedsWasmFlag: Boolean = true + // Pre-V8 13.x runtimes need --experimental-wasm-exnref for the Scala.js Wasm exception model. + // V8 13.x ships in Node 25+ (Node 24 is still on V8 12.x where exnref is gated behind the flag). + // In Node 26+, the flag may be removed from the CLI. Only pass it when Node < 25. + // None.forall(_ < 25) == true — safe fallback when version detection fails. + private def nodeNeedsWasmFlag: Boolean = nodeMajorVersion.forall(_ < 25) // Deno 2.x bundles V8 12.x where wasm-exnref is gated behind a flag; symmetrical reasoning to Node. - // We always set DENO_V8_FLAGS=--experimental-wasm-exnref on emitWasm until V8 13.x lands in Deno. + // We always set DENO_V8_FLAGS=--experimental-wasm-exnref on Wasm output until V8 13.x lands in Deno. private def denoNeedsWasmFlag: Boolean = true - // Detects the major version of Bun on PATH; cached for the JVM lifetime (lazy val). - // Returns None if bun is not found or version cannot be parsed. - private lazy val bunMajorVersion: Option[Int] = - try { - val process = new ProcessBuilder("bun", "--version") - .redirectErrorStream(true) - .start() - val output = new String(process.getInputStream.readAllBytes()).trim - process.waitFor() - // Bun version format: "1.1.30" -> extract 1 - output.takeWhile(_.isDigit) match { - case s if s.nonEmpty => Some(s.toInt) - case _ => None - } - } - catch { - case _: Exception => None - } - private def endsWithCaseInsensitive(s: String, suffix: String): Boolean = s.length >= suffix.length && s.regionMatches(true, s.length - suffix.length, suffix, 0, suffix.length) @@ -300,10 +282,6 @@ object Runner { value(findInPath("node") .map(_.toString) .toRight(NodeNotFoundError())) - if (emitWasm) - nodeMajorVersion.foreach { v => - if (v < 22) value(Left(new NodeVersionTooOldForWasmError(v))) - } val nodeFlags = if (emitWasm && nodeNeedsWasmFlag) List("--experimental-wasm-exnref") else Nil if !jsDom && allowExecve && Execve.available() then { val command = Seq(nodePath) ++ nodeFlags ++ Seq(entrypoint.getAbsolutePath) ++ args @@ -322,19 +300,6 @@ object Runner { ) sys.error("should not happen") } - else if (emitWasm) { - // For WASM mode with ES modules, run node directly instead of NodeJSEnv. - // NodeJSEnv's stdin piping with "-" doesn't work with Input.ESModule. - val command = Seq(nodePath) ++ nodeFlags ++ Seq(entrypoint.getAbsolutePath) ++ args - - logger.log( - s"Running ${command.mkString(" ")}", - " Running" + System.lineSeparator() + - command.iterator.map(_ + System.lineSeparator()).mkString - ) - - new ProcessBuilder(command: _*).inheritIO().start() - } else { val nodeArgs = // Scala.js runs apps by piping JS to node. @@ -455,10 +420,6 @@ object Runner { value(findInPath("bun") .map(_.toString) .toRight(BunNotFoundError())) - bunMajorVersion.foreach { v => - if (v < 1) value(Left(new BunVersionTooOldForWasmError(v))) - } - val command = Seq(bunPath, "run", entrypoint.getAbsolutePath) ++ args if (allowExecve && Execve.available()) { diff --git a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala index 4e04a3d798..0a4467ee86 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala @@ -474,12 +474,12 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { if shouldLogCrossInfo then logger.debug(s"Running build for ${crossBuildParams.asString}") val build = builds.head either { - val wasmOpts = build.options.wasmOptions + val jsOpts = build.options.scalaJsOptions - // Check if WASM mode is requested - if wasmOpts.enabled then { - val runtime = wasmOpts.runtime - val esModule = true // WASM backend uses ES modules + // Check if Wasm mode is requested + if jsOpts.jsEmitWasm then { + val runtime = jsOpts.wasmRuntime + val esModule = true // Wasm backend uses ES modules scratchDirOpt.foreach(os.makeDir.all(_)) val jsDest = os.temp( dir = scratchDirOpt.orNull, @@ -488,8 +488,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { deleteOnExit = scratchDirOpt.isEmpty ) - val linkerConfig = build.options.scalaJsOptions.linkerConfig(logger) - .copy(emitWasm = true, moduleKind = ScalaJsLinkerConfig.ModuleKind.ESModule) + val linkerConfig = jsOpts.linkerConfig(logger) val res = Package.linkJs( builds = builds, @@ -497,8 +496,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { mainClassOpt = Some(mainClass), addTestInitializer = false, config = linkerConfig, - fullOpt = value(build.options.scalaJsOptions.fullOpt), - noOpt = build.options.scalaJsOptions.noOpt.getOrElse(false), + fullOpt = value(jsOpts.fullOpt), + noOpt = jsOpts.noOpt.getOrElse(false), logger = logger, scratchDirOpt = scratchDirOpt ).map { outputPath => @@ -529,7 +528,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { logger, allowExecve = effectiveAllowExecve, jsDom = false, - sourceMap = build.options.scalaJsOptions.emitSourceMaps, + sourceMap = jsOpts.emitSourceMaps, esModule = esModule, emitWasm = true ) diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/ScalaJsOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/ScalaJsOptions.scala index 96045c4eec..2a8aae990e 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/ScalaJsOptions.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/ScalaJsOptions.scala @@ -59,11 +59,16 @@ final case class ScalaJsOptions( @HelpMessage("Enable jsdom") jsDom: Option[Boolean] = None, - @Group(HelpGroup.ScalaJs.toString) + @Group(HelpGroup.Wasm.toString) @Tag(tags.experimental) - @HelpMessage("Emit WASM") + @HelpMessage("Enable Wasm output (Scala.js Wasm backend). Uses Node.js by default. To show more options for Wasm pass `--help-wasm`") jsEmitWasm: Option[Boolean] = None, + @Group(HelpGroup.Wasm.toString) + @Tag(tags.experimental) + @HelpMessage("Wasm runtime to use: node (default), deno, bun") + jsWasmRuntime: Option[String] = None, + @Group(HelpGroup.ScalaJs.toString) @Tag(tags.should) @HelpMessage("A header that will be added at the top of generated .js files") diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala index 9c988b6dca..d81a8b8f48 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala @@ -57,8 +57,6 @@ final case class SharedOptions( js: ScalaJsOptions = ScalaJsOptions(), @Recurse native: ScalaNativeOptions = ScalaNativeOptions(), - @Recurse - wasmOptions: WasmOptions = WasmOptions(), @Recurse compilationServer: SharedCompilationServerOptions = SharedCompilationServerOptions(), @Recurse @@ -249,26 +247,40 @@ final case class SharedOptions( ) .getOrElse(true) - private def scalaJsOptions(opts: ScalaJsOptions): options.ScalaJsOptions = { + private def scalaJsOptions( + opts: ScalaJsOptions + ): Either[BuildException, options.ScalaJsOptions] = { import opts._ - options.ScalaJsOptions( - version = jsVersion, - mode = options.ScalaJsMode(jsMode), - moduleKindStr = jsModuleKind, - checkIr = jsCheckIr, - emitSourceMaps = jsEmitSourceMaps, - sourceMapsDest = jsSourceMapsPath.filter(_.trim.nonEmpty).map(os.Path(_, Os.pwd)), - dom = jsDom, - header = jsHeader, - allowBigIntsForLongs = jsAllowBigIntsForLongs, - avoidClasses = jsAvoidClasses, - avoidLetsAndConsts = jsAvoidLetsAndConsts, - moduleSplitStyleStr = jsModuleSplitStyle, - smallModuleForPackage = jsSmallModuleForPackage, - esVersionStr = jsEsVersion, - noOpt = jsNoOpt, - remapEsModuleImportMap = jsEsModuleImportMap.filter(_.trim.nonEmpty).map(os.Path(_, Os.pwd)), - jsEmitWasm = jsEmitWasm.getOrElse(false) + val parsedWasmRuntime = jsWasmRuntime.fold( + Right(options.WasmRuntime.default): Either[BuildException, options.WasmRuntime] + ) { rt => + options.WasmRuntime.parse(rt).toRight { + val validValues = options.WasmRuntime.all.map(_.name).mkString(", ") + new scala.build.errors.UnrecognizedWasmRuntimeError(rt, validValues) + } + } + parsedWasmRuntime.map(wasmRuntime => + options.ScalaJsOptions( + version = jsVersion, + mode = options.ScalaJsMode(jsMode), + moduleKindStr = jsModuleKind, + checkIr = jsCheckIr, + emitSourceMaps = jsEmitSourceMaps, + sourceMapsDest = jsSourceMapsPath.filter(_.trim.nonEmpty).map(os.Path(_, Os.pwd)), + dom = jsDom, + header = jsHeader, + allowBigIntsForLongs = jsAllowBigIntsForLongs, + avoidClasses = jsAvoidClasses, + avoidLetsAndConsts = jsAvoidLetsAndConsts, + moduleSplitStyleStr = jsModuleSplitStyle, + smallModuleForPackage = jsSmallModuleForPackage, + esVersionStr = jsEsVersion, + noOpt = jsNoOpt, + remapEsModuleImportMap = + jsEsModuleImportMap.filter(_.trim.nonEmpty).map(os.Path(_, Os.pwd)), + jsEmitWasm = jsEmitWasm.getOrElse(false), + wasmRuntime = wasmRuntime + ) ) } @@ -313,26 +325,6 @@ final case class SharedOptions( ) } - private def buildWasmOptions( - opts: WasmOptions - ): Either[BuildException, options.WasmOptions] = { - import opts._ - val wasmEnabled = wasm || wasmRuntime.isDefined - val parsedRuntime: Either[BuildException, options.WasmRuntime] = - wasmRuntime.fold(Right(options.WasmRuntime.default)) { rt => - options.WasmRuntime.parse(rt).toRight { - val validValues = options.WasmRuntime.all.map(_.name).mkString(", ") - new scala.build.errors.UnrecognizedWasmRuntimeError(rt, validValues) - } - } - parsedRuntime.map(runtime => - options.WasmOptions( - enabled = wasmEnabled, - runtime = runtime - ) - ) - } - lazy val scalacOptionsFromFiles: List[String] = scalac.argsFiles.flatMap(argFile => ArgSplitter.splitToArgs(os.read(os.Path(argFile.file, os.pwd))) @@ -357,8 +349,8 @@ final case class SharedOptions( case _ => } val parsedPlatform = platform.map(Platform.normalize).flatMap(Platform.parse) - // WASM mode requires Scala.js platform for compilation - val wasmEnabled = wasmOptions.wasm || wasmOptions.wasmRuntime.isDefined + // Wasm mode requires Scala.js platform for compilation + val wasmEnabled = js.jsEmitWasm.getOrElse(false) || js.jsWasmRuntime.isDefined val platformOpt = value { (parsedPlatform, js.js, native.native, wasmEnabled) match { case (Some(p: Platform.JS.type), _, false, _) => Right(Some(p)) @@ -372,10 +364,10 @@ final case class SharedOptions( case (_, true, true, _) => Left(new AmbiguousPlatformError(Seq(Platform.JS.toString, Platform.Native.toString))) case (_, _, true, true) => - Left(new AmbiguousPlatformError(Seq(Platform.Native.toString, "WASM (requires JS)"))) + Left(new AmbiguousPlatformError(Seq(Platform.Native.toString, "Wasm (requires JS)"))) case (_, true, _, _) => Right(Some(Platform.JS)) case (_, _, _, true) => - Right(Some(Platform.JS)) // WASM requires JS compilation (Scala.js WASM backend) + Right(Some(Platform.JS)) // Wasm requires JS compilation (Scala.js Wasm backend) case (_, _, true, _) => Right(Some(Platform.Native)) case _ => Right(None) } @@ -462,9 +454,8 @@ final case class SharedOptions( scriptOptions = scala.build.options.ScriptOptions( forceObjectWrapper = objectWrapper ), - scalaJsOptions = scalaJsOptions(js), + scalaJsOptions = value(scalaJsOptions(js)), scalaNativeOptions = snOpts, - wasmOptions = value(buildWasmOptions(wasmOptions)), javaOptions = value(scala.cli.commands.util.JvmUtils.javaOptions(jvm)), jmhOptions = scala.build.options.JmhOptions( jmhVersion = benchmarking.jmhVersion, diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala deleted file mode 100644 index da99420d95..0000000000 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/WasmOptions.scala +++ /dev/null @@ -1,27 +0,0 @@ -package scala.cli.commands.shared - -import caseapp.* -import com.github.plokhotnyuk.jsoniter_scala.core.* -import com.github.plokhotnyuk.jsoniter_scala.macros.* - -import scala.cli.commands.tags - -// format: off -final case class WasmOptions( - @Group(HelpGroup.Wasm.toString) - @Tag(tags.experimental) - @HelpMessage("Enable WebAssembly output (Scala.js WASM backend). Uses Node.js by default. To show more options for WASM pass `--help-wasm`") - wasm: Boolean = false, - - @Group(HelpGroup.Wasm.toString) - @Tag(tags.experimental) - @HelpMessage("WASM runtime to use: node (default), deno, bun") - wasmRuntime: Option[String] = None -) -// format: on - -object WasmOptions { - implicit lazy val parser: Parser[WasmOptions] = Parser.derive - implicit lazy val help: Help[WasmOptions] = Help.derive - implicit lazy val jsonCodec: JsonValueCodec[WasmOptions] = JsonCodecMaker.make -} diff --git a/modules/core/src/main/scala/scala/build/errors/BunNotFoundError.scala b/modules/core/src/main/scala/scala/build/errors/BunNotFoundError.scala index d5db2865a6..a67aaa586b 100644 --- a/modules/core/src/main/scala/scala/build/errors/BunNotFoundError.scala +++ b/modules/core/src/main/scala/scala/build/errors/BunNotFoundError.scala @@ -1,5 +1,5 @@ package scala.build.errors final class BunNotFoundError extends BuildException( - "Bun was not found on the PATH. Install Bun from https://bun.sh/ or use --wasm-runtime node" + "Bun was not found on the PATH. Install Bun from https://bun.sh/ or use --js-wasm-runtime node" ) diff --git a/modules/core/src/main/scala/scala/build/errors/BunVersionTooOldForWasmError.scala b/modules/core/src/main/scala/scala/build/errors/BunVersionTooOldForWasmError.scala deleted file mode 100644 index 5386aa56a6..0000000000 --- a/modules/core/src/main/scala/scala/build/errors/BunVersionTooOldForWasmError.scala +++ /dev/null @@ -1,7 +0,0 @@ -package scala.build.errors - -final class BunVersionTooOldForWasmError(found: Int) - extends BuildException( - s"Scala.js WASM backend requires Bun >= 1, but found Bun $found. " + - "Upgrade Bun (https://bun.sh/) or switch runtime via --wasm-runtime node|deno." - ) diff --git a/modules/core/src/main/scala/scala/build/errors/DenoNotFoundError.scala b/modules/core/src/main/scala/scala/build/errors/DenoNotFoundError.scala index 4566e346a4..cf9dc42b78 100644 --- a/modules/core/src/main/scala/scala/build/errors/DenoNotFoundError.scala +++ b/modules/core/src/main/scala/scala/build/errors/DenoNotFoundError.scala @@ -1,5 +1,5 @@ package scala.build.errors final class DenoNotFoundError extends BuildException( - "Deno was not found on the PATH. Install Deno from https://deno.land/ or use --wasm-runtime node" + "Deno was not found on the PATH. Install Deno from https://deno.land/ or use --js-wasm-runtime node" ) diff --git a/modules/core/src/main/scala/scala/build/errors/NodeVersionTooOldForWasmError.scala b/modules/core/src/main/scala/scala/build/errors/NodeVersionTooOldForWasmError.scala deleted file mode 100644 index d1a48562d9..0000000000 --- a/modules/core/src/main/scala/scala/build/errors/NodeVersionTooOldForWasmError.scala +++ /dev/null @@ -1,7 +0,0 @@ -package scala.build.errors - -final class NodeVersionTooOldForWasmError(found: Int) - extends BuildException( - s"Scala.js WASM backend requires Node.js >= 22, but found Node.js $found. " + - "Upgrade Node (https://nodejs.org/) or switch runtime via --wasm-runtime deno|bun." - ) diff --git a/modules/core/src/main/scala/scala/build/errors/UnrecognizedWasmRuntimeError.scala b/modules/core/src/main/scala/scala/build/errors/UnrecognizedWasmRuntimeError.scala index 46e2f43b6c..2d7b01db36 100644 --- a/modules/core/src/main/scala/scala/build/errors/UnrecognizedWasmRuntimeError.scala +++ b/modules/core/src/main/scala/scala/build/errors/UnrecognizedWasmRuntimeError.scala @@ -1,4 +1,4 @@ package scala.build.errors class UnrecognizedWasmRuntimeError(runtime: String, validValues: String) - extends BuildException(s"Unrecognized WASM runtime: '$runtime'. Valid values: $validValues") + extends BuildException(s"Unrecognized Wasm runtime: '$runtime'. Valid values: $validValues") diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala index d8c959f6fb..932fafee89 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala @@ -3,10 +3,10 @@ package scala.build.preprocessing.directives import scala.build.Positioned import scala.build.directives.* import scala.build.errors.{BuildException, UnrecognizedWasmRuntimeError} -import scala.build.options.{BuildOptions, Platform, ScalaOptions, WasmOptions, WasmRuntime} +import scala.build.options.{BuildOptions, Platform, ScalaJsOptions, ScalaOptions, WasmRuntime} import scala.cli.commands.SpecificationLevel -@DirectiveGroupName("WASM options") +@DirectiveGroupName("Wasm options") @DirectiveExamples("//> using wasm") @DirectiveExamples("//> using wasmRuntime node") @DirectiveExamples("//> using wasmRuntime deno") @@ -37,17 +37,16 @@ final case class Wasm( } parsedRuntime.map { runtime => val wasmEnabled = wasm.getOrElse(false) || wasmRuntime.isDefined - val wasmOptions = WasmOptions( - enabled = wasmEnabled, - runtime = runtime - ) - // When WASM is enabled, force Platform.JS (Scala.js WASM backend requires JS compilation) + // When Wasm is enabled, force Platform.JS (Scala.js Wasm backend requires JS compilation) val scalaOptions = if (wasmEnabled) ScalaOptions(platform = Some(Positioned.none(Platform.JS))) else ScalaOptions() - BuildOptions(scalaOptions = scalaOptions, wasmOptions = wasmOptions) + BuildOptions( + scalaOptions = scalaOptions, + scalaJsOptions = ScalaJsOptions(jsEmitWasm = wasmEnabled, wasmRuntime = runtime) + ) } } } diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala index 962091e74f..ba747a7a8b 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala @@ -328,7 +328,7 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => } } - test("Run with --wasm flag") { + test("Run with --js-emit-wasm flag") { val inputs = TestInputs( os.rel / "Hello.scala" -> """object Hello { @@ -342,8 +342,8 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => "--power", "run", "Hello.scala", - "--wasm", - "--wasm-runtime", + "--js-emit-wasm", + "--js-wasm-runtime", "node", extraOptions ).call(cwd = root).out.trim() @@ -351,7 +351,7 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => } } - test("Run with --wasm uses Node.js by default") { + test("Run with --js-emit-wasm uses Node.js by default") { val inputs = TestInputs( os.rel / "Hello.scala" -> """object Hello { @@ -365,7 +365,7 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => "--power", "run", "Hello.scala", - "--wasm", + "--js-emit-wasm", extraOptions ).call(cwd = root).out.trim() expect(output == "Hello default WASM!") @@ -394,7 +394,7 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => } } - test("WASM passes arguments to program") { + test("Wasm passes arguments to program") { // Scala.js always passes an empty Array[String] to main(args), // so we must read process.argv directly via JS interop. val inputs = TestInputs( @@ -415,8 +415,8 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => "--power", "run", "Hello.scala", - "--wasm", - "--wasm-runtime", + "--js-emit-wasm", + "--js-wasm-runtime", "node", extraOptions, "--", @@ -429,7 +429,7 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => } if (TestUtil.fromPath("deno").isDefined) - test("Run with --wasm-runtime deno") { + test("Run with --js-wasm-runtime deno") { val inputs = TestInputs( os.rel / "Hello.scala" -> """object Hello { @@ -443,8 +443,8 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => "--power", "run", "Hello.scala", - "--wasm", - "--wasm-runtime", + "--js-emit-wasm", + "--js-wasm-runtime", "deno", extraOptions ).call(cwd = root).out.trim() @@ -453,7 +453,7 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => } if (TestUtil.fromPath("bun").isDefined) - test("Run with --wasm-runtime bun") { + test("Run with --js-wasm-runtime bun") { val inputs = TestInputs( os.rel / "Hello.scala" -> """object Hello { @@ -467,8 +467,8 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => "--power", "run", "Hello.scala", - "--wasm", - "--wasm-runtime", + "--js-emit-wasm", + "--js-wasm-runtime", "bun", extraOptions ).call(cwd = root).out.trim() @@ -476,7 +476,7 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => } } - test("WASM multiple source files") { + test("Wasm multiple source files") { val inputs = TestInputs( os.rel / "Greeter.scala" -> """trait Greeter { @@ -502,8 +502,8 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => "run", "Main.scala", "Greeter.scala", - "--wasm", - "--wasm-runtime", + "--js-emit-wasm", + "--js-wasm-runtime", "node", extraOptions ).call(cwd = root).out.trim() @@ -511,7 +511,7 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => } } - test("WASM exception handling") { + test("Wasm exception handling") { val inputs = TestInputs( os.rel / "Hello.scala" -> """object Hello { @@ -534,8 +534,8 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => "--power", "run", "Hello.scala", - "--wasm", - "--wasm-runtime", + "--js-emit-wasm", + "--js-wasm-runtime", "node", extraOptions ).call(cwd = root).out.trim() @@ -545,7 +545,7 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => } } - test("WASM collections and higher-order functions") { + test("Wasm collections and higher-order functions") { val inputs = TestInputs( os.rel / "Hello.scala" -> """object Hello { @@ -566,8 +566,8 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => "--power", "run", "Hello.scala", - "--wasm", - "--wasm-runtime", + "--js-emit-wasm", + "--js-wasm-runtime", "node", extraOptions ).call(cwd = root).out.trim() @@ -579,7 +579,7 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => } if (!actualScalaVersion.startsWith("2")) - test("WASM @main annotation (Scala 3)") { + test("Wasm @main annotation (Scala 3)") { // Scala.js always passes empty args to main, so @main with parameters won't work. // Test @main without parameters instead. val inputs = TestInputs( @@ -594,8 +594,8 @@ trait RunScalaJsTestDefinitions { this: RunTestDefinitions => "--power", "run", "Hello.scala", - "--wasm", - "--wasm-runtime", + "--js-emit-wasm", + "--js-wasm-runtime", "node", extraOptions ).call(cwd = root).out.trim() diff --git a/modules/options/src/main/scala/scala/build/options/BuildOptions.scala b/modules/options/src/main/scala/scala/build/options/BuildOptions.scala index 9bc4a8b006..f0f09bf26b 100644 --- a/modules/options/src/main/scala/scala/build/options/BuildOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/BuildOptions.scala @@ -37,7 +37,6 @@ final case class BuildOptions( scalaOptions: ScalaOptions = ScalaOptions(), scalaJsOptions: ScalaJsOptions = ScalaJsOptions(), scalaNativeOptions: ScalaNativeOptions = ScalaNativeOptions(), - wasmOptions: WasmOptions = WasmOptions(), internalDependencies: InternalDependenciesOptions = InternalDependenciesOptions(), javaOptions: JavaOptions = JavaOptions(), jmhOptions: JmhOptions = JmhOptions(), diff --git a/modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala b/modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala index ccaef26c33..b4830acfd1 100644 --- a/modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala @@ -26,7 +26,8 @@ final case class ScalaJsOptions( smallModuleForPackage: List[String] = Nil, esVersionStr: Option[String] = None, noOpt: Option[Boolean] = None, - jsEmitWasm: Boolean = false + jsEmitWasm: Boolean = false, + wasmRuntime: WasmRuntime = WasmRuntime.default ) { def fullOpt: Either[UnrecognizedJsOptModeError, Boolean] = if (mode.isValid) @@ -155,7 +156,8 @@ final case class ScalaJsOptions( ) ScalaJsLinkerConfig( - moduleKind = moduleKind(logger), + moduleKind = + if (jsEmitWasm) ScalaJsLinkerConfig.ModuleKind.ESModule else moduleKind(logger), checkIR = checkIr.getOrElse(false), // meh sourceMap = emitSourceMaps, moduleSplitStyle = moduleSplitStyle(logger), diff --git a/modules/options/src/main/scala/scala/build/options/WasmOptions.scala b/modules/options/src/main/scala/scala/build/options/WasmOptions.scala deleted file mode 100644 index 34450e2385..0000000000 --- a/modules/options/src/main/scala/scala/build/options/WasmOptions.scala +++ /dev/null @@ -1,18 +0,0 @@ -package scala.build.options - -/** Options for WebAssembly compilation and execution. - * - * @param enabled - * If true, enable WASM output (Scala.js WASM backend) - * @param runtime - * The WASM runtime to use for execution (node, deno) - */ -final case class WasmOptions( - enabled: Boolean = false, - runtime: WasmRuntime = WasmRuntime.default -) - -object WasmOptions { - implicit val hasHashData: HasHashData[WasmOptions] = HasHashData.derive - implicit val monoid: ConfigMonoid[WasmOptions] = ConfigMonoid.derive -} diff --git a/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala b/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala index b5f8814e5d..5afae2d49f 100644 --- a/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala +++ b/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala @@ -4,7 +4,7 @@ import java.util.Locale /** Represents available WebAssembly runtimes for execution. * - * JS-based runtimes (work now with Scala.js WASM backend): + * JS-based runtimes (work now with Scala.js Wasm backend): * - Node: Uses Node.js (V8 engine) with JavaScript loader * - Deno: Uses Deno (V8 engine) with ES module support * - Bun: Uses Bun (JavaScriptCore engine) with ES module support diff --git a/website/docs/reference/cli-options.md b/website/docs/reference/cli-options.md index 1909961585..f80e922164 100644 --- a/website/docs/reference/cli-options.md +++ b/website/docs/reference/cli-options.md @@ -1379,7 +1379,11 @@ Enable jsdom ### `--js-emit-wasm` -Emit WASM +Enable Wasm output (Scala.js Wasm backend). Uses Node.js by default. To show more options for Wasm pass `--help-wasm` + +### `--js-wasm-runtime` + +Wasm runtime to use: node (default), deno, bun ### `--js-header` @@ -1972,22 +1976,6 @@ A github token used to access GitHub. Not needed in most cases. Don't check for the newest available Scala CLI version upstream -## Wasm options - -Available in commands: - -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) - - - -### `--wasm` - -Enable WebAssembly output (Scala.js WASM backend). Uses Node.js by default. To show more options for WASM pass `--help-wasm` - -### `--wasm-runtime` - -WASM runtime to use: node (default), deno, bun - ## Watch options Available in commands: diff --git a/website/docs/reference/commands.md b/website/docs/reference/commands.md index 643f0cd72e..9aaeff82ed 100644 --- a/website/docs/reference/commands.md +++ b/website/docs/reference/commands.md @@ -32,7 +32,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/compile -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [compile](./cli-options.md#compile-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [compile](./cli-options.md#compile-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## config @@ -83,7 +83,7 @@ Accepts option groups: [config](./cli-options.md#config-options), [coursier](./c Update dependency directives in the project -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [dependency update](./cli-options.md#dependency-update-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [dependency update](./cli-options.md#dependency-update-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) ## doc @@ -97,7 +97,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/doc -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [doc](./cli-options.md#doc-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [doc](./cli-options.md#doc-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) ## export @@ -119,7 +119,7 @@ The `export` sub-command is experimental. Please bear in mind that non-ideal user experience should be expected. If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [export](./cli-options.md#export-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [export](./cli-options.md#export-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) ## fix @@ -145,7 +145,7 @@ The `fix` sub-command is experimental. Please bear in mind that non-ideal user experience should be expected. If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fix](./cli-options.md#fix-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [Scalafix](./cli-options.md#scalafix-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fix](./cli-options.md#fix-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [Scalafix](./cli-options.md#scalafix-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) ## fmt @@ -162,7 +162,7 @@ All standard Scala CLI inputs are accepted, but only Scala sources will be forma For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/fmt -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fmt](./cli-options.md#fmt-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fmt](./cli-options.md#fmt-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) ## help @@ -214,7 +214,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/repl -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [repl](./cli-options.md#repl-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [repl](./cli-options.md#repl-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## package @@ -232,7 +232,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/package -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [package](./cli-options.md#package-options), [packager](./cli-options.md#packager-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [package](./cli-options.md#package-options), [packager](./cli-options.md#packager-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## publish @@ -259,7 +259,7 @@ The `publish` sub-command is experimental. Please bear in mind that non-ideal user experience should be expected. If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [publish](./cli-options.md#publish-options), [publish connection](./cli-options.md#publish-connection-options), [publish params](./cli-options.md#publish-params-options), [publish repository](./cli-options.md#publish-repository-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [publish](./cli-options.md#publish-options), [publish connection](./cli-options.md#publish-connection-options), [publish params](./cli-options.md#publish-params-options), [publish repository](./cli-options.md#publish-repository-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## publish local @@ -271,7 +271,7 @@ The `publish-local` sub-command is experimental. Please bear in mind that non-ideal user experience should be expected. If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [publish](./cli-options.md#publish-options), [publish local](./cli-options.md#publish-local-options), [publish params](./cli-options.md#publish-params-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [publish](./cli-options.md#publish-options), [publish local](./cli-options.md#publish-local-options), [publish params](./cli-options.md#publish-params-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## publish setup @@ -309,7 +309,7 @@ To pass arguments to the actual application, just add them after `--`, like: For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/run -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## github secret create @@ -356,7 +356,7 @@ Using directives can be defined in all supported input source file types. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/setup-ide -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp file](./cli-options.md#bsp-file-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [setup IDE](./cli-options.md#setup-ide-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp file](./cli-options.md#bsp-file-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [setup IDE](./cli-options.md#setup-ide-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) ## shebang @@ -387,7 +387,7 @@ Using this, it is possible to conveniently set up Unix shebang scripts. For exam For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/shebang -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## test @@ -411,7 +411,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/test -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [test](./cli-options.md#test-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [test](./cli-options.md#test-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## uninstall @@ -514,7 +514,7 @@ It is normally supposed to be invoked by your IDE when a Scala CLI project is im Detailed documentation can be found on our website: https://scala-cli.virtuslab.org -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp](./cli-options.md#bsp-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp](./cli-options.md#bsp-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) ### default-file diff --git a/website/docs/reference/directives.md b/website/docs/reference/directives.md index d9640d57bf..02dfb2f135 100644 --- a/website/docs/reference/directives.md +++ b/website/docs/reference/directives.md @@ -707,7 +707,7 @@ Use a toolkit as dependency (not supported in Scala 2.12), 'default' version for `//> using test.toolkit default` -### WASM options +### Wasm options Add WebAssembly options diff --git a/website/docs/reference/scala-command/cli-options.md b/website/docs/reference/scala-command/cli-options.md index bd8ebaf16a..91916db057 100644 --- a/website/docs/reference/scala-command/cli-options.md +++ b/website/docs/reference/scala-command/cli-options.md @@ -1495,16 +1495,6 @@ A github token used to access GitHub. Not needed in most cases. Don't check for the newest available Scala CLI version upstream -## Wasm options - -Available in commands: - -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) - - - -*This section was automatically generated and may be empty if no options were available.* - ## Watch options Available in commands: diff --git a/website/docs/reference/scala-command/commands.md b/website/docs/reference/scala-command/commands.md index e7fbd71e5a..8b7c42a9eb 100644 --- a/website/docs/reference/scala-command/commands.md +++ b/website/docs/reference/scala-command/commands.md @@ -31,7 +31,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/compile -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [compile](./cli-options.md#compile-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [compile](./cli-options.md#compile-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ### config @@ -90,7 +90,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/doc -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [doc](./cli-options.md#doc-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [doc](./cli-options.md#doc-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) ### repl @@ -112,7 +112,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/repl -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [repl](./cli-options.md#repl-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [repl](./cli-options.md#repl-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ### run @@ -138,7 +138,7 @@ To pass arguments to the actual application, just add them after `--`, like: For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/run -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ### shebang @@ -169,7 +169,7 @@ Using this, it is possible to conveniently set up Unix shebang scripts. For exam For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/shebang -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ## SHOULD have commands: @@ -188,7 +188,7 @@ All standard Scala CLI inputs are accepted, but only Scala sources will be forma For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/fmt -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fmt](./cli-options.md#fmt-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fmt](./cli-options.md#fmt-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) ### test @@ -212,7 +212,7 @@ All supported types of inputs can be mixed with each other. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/test -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [test](./cli-options.md#test-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [test](./cli-options.md#test-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options) ### version @@ -244,7 +244,7 @@ It is normally supposed to be invoked by your IDE when a Scala CLI project is im Detailed documentation can be found on our website: https://scala-cli.virtuslab.org -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp](./cli-options.md#bsp-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp](./cli-options.md#bsp-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) ### clean @@ -296,7 +296,7 @@ Using directives can be defined in all supported input source file types. For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/setup-ide -Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp file](./cli-options.md#bsp-file-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [setup IDE](./cli-options.md#setup-ide-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [wasm](./cli-options.md#wasm-options), [workspace](./cli-options.md#workspace-options) +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp file](./cli-options.md#bsp-file-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [setup IDE](./cli-options.md#setup-ide-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) ### uninstall From 82e51366dbc2dbf65ef0f808cbe41616d54b3b80 Mon Sep 17 00:00:00 2001 From: lostflydev Date: Sat, 30 May 2026 12:28:26 +0300 Subject: [PATCH 6/9] Add logging for implicit Wasm behaviors; rebase on upstream main - Log when --experimental-wasm-exnref is injected in runJs (Node.js < 25) - Log when Wasm mode enables ES module output in Run.scala - Improve comment in Wasm.scala directive to reference AmbiguousPlatformError for --wasm + --platform native conflict detection --- .../build/src/main/scala/scala/build/internal/Runner.scala | 4 ++++ modules/cli/src/main/scala/scala/cli/commands/run/Run.scala | 1 + .../scala/scala/build/preprocessing/directives/Wasm.scala | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/build/src/main/scala/scala/build/internal/Runner.scala b/modules/build/src/main/scala/scala/build/internal/Runner.scala index d5c4d7585c..16367efab4 100644 --- a/modules/build/src/main/scala/scala/build/internal/Runner.scala +++ b/modules/build/src/main/scala/scala/build/internal/Runner.scala @@ -283,6 +283,10 @@ object Runner { .map(_.toString) .toRight(NodeNotFoundError())) val nodeFlags = if (emitWasm && nodeNeedsWasmFlag) List("--experimental-wasm-exnref") else Nil + if (emitWasm && nodeFlags.nonEmpty) + logger.log( + s"Wasm: adding ${nodeFlags.mkString(" ")} (required for Wasm exception handling on Node.js < 25)" + ) if !jsDom && allowExecve && Execve.available() then { val command = Seq(nodePath) ++ nodeFlags ++ Seq(entrypoint.getAbsolutePath) ++ args diff --git a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala index 0a4467ee86..8e17ab0762 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala @@ -480,6 +480,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { if jsOpts.jsEmitWasm then { val runtime = jsOpts.wasmRuntime val esModule = true // Wasm backend uses ES modules + logger.log("Wasm mode enabled: using ES module output on JS platform") scratchDirOpt.foreach(os.makeDir.all(_)) val jsDest = os.temp( dir = scratchDirOpt.orNull, diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala index 932fafee89..e465f799cc 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala @@ -37,7 +37,8 @@ final case class Wasm( } parsedRuntime.map { runtime => val wasmEnabled = wasm.getOrElse(false) || wasmRuntime.isDefined - // When Wasm is enabled, force Platform.JS (Scala.js Wasm backend requires JS compilation) + // Scala.js Wasm backend requires JS platform. When --platform native is also + // specified alongside --wasm, an AmbiguousPlatformError is raised in platform resolution. val scalaOptions = if (wasmEnabled) ScalaOptions(platform = Some(Positioned.none(Platform.JS))) From f692f1960944e3b4b19720b00fbc3ff7ce67b725 Mon Sep 17 00:00:00 2001 From: lostflydev Date: Sat, 30 May 2026 13:33:55 +0300 Subject: [PATCH 7/9] Fix DENO_V8_FLAGS override and add warning for --js-module-kind + Wasm - runDeno: append --experimental-wasm-exnref to existing DENO_V8_FLAGS instead of silently replacing user-defined flags; log when flag is set - ScalaJsOptions.linkerConfig: warn when Wasm overrides user-specified --js-module-kind (forced to ESModule by Wasm backend) --- .../main/scala/scala/build/internal/Runner.scala | 13 +++++++++++-- .../scala/scala/build/options/ScalaJsOptions.scala | 4 ++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/internal/Runner.scala b/modules/build/src/main/scala/scala/build/internal/Runner.scala index 16367efab4..b524ac705f 100644 --- a/modules/build/src/main/scala/scala/build/internal/Runner.scala +++ b/modules/build/src/main/scala/scala/build/internal/Runner.scala @@ -367,9 +367,18 @@ object Runner { .map(_.toString) .toRight(DenoNotFoundError())) val denoFlags = Seq("run", "--allow-read") + val wasmFlag = "--experimental-wasm-exnref" val extraEnv = - if (emitWasm && denoNeedsWasmFlag) Map("DENO_V8_FLAGS" -> "--experimental-wasm-exnref") - else Map.empty + if (emitWasm && denoNeedsWasmFlag) { + // Append to any existing DENO_V8_FLAGS rather than replacing them. + val existing = sys.env.get("DENO_V8_FLAGS").filter(_.nonEmpty) + val merged = existing.fold(wasmFlag)(f => s"$f $wasmFlag") + logger.log( + s"Wasm: setting DENO_V8_FLAGS=$merged (required for Wasm exception handling)" + ) + Map("DENO_V8_FLAGS" -> merged) + } + else Map.empty[String, String] if (allowExecve && Execve.available()) { val command = Seq(denoPath) ++ denoFlags ++ Seq(entrypoint.getAbsolutePath) ++ args diff --git a/modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala b/modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala index b4830acfd1..f162de8526 100644 --- a/modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala @@ -155,6 +155,10 @@ final case class ScalaJsOptions( esVersion = esVersion(logger) ) + if (jsEmitWasm && moduleKindStr.isDefined) + logger.message( + s"[${Console.YELLOW}warn${Console.RESET}] Wasm mode forces ES module output; --js-module-kind is ignored" + ) ScalaJsLinkerConfig( moduleKind = if (jsEmitWasm) ScalaJsLinkerConfig.ModuleKind.ESModule else moduleKind(logger), From eabfdbeef9cd43482e7098b867c2f2fa5688991e Mon Sep 17 00:00:00 2001 From: lostflydev Date: Sun, 31 May 2026 11:58:37 +0300 Subject: [PATCH 8/9] Restore SbtFile and null guards in JVM sourceFiles match Lost during Run.scala restructure for Wasm: restores the original case _: SbtFile => "" and case null => "" branches so SbtFile inputs are properly filtered from scala.sources JVM property. --- modules/cli/src/main/scala/scala/cli/commands/run/Run.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala index 8e17ab0762..b4ba60a090 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala @@ -677,8 +677,9 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { case s: ScalaFile => fwd(s.path.toString) case s: Script => fwd(s.path.toString) case s: MarkdownFile => fwd(s.path.toString) + case _: SbtFile => "" case s: OnDisk => fwd(s.path.toString) - case s => s.getClass.getName + case null => "" }.filter(_.nonEmpty).distinct val sources = sourceFiles.mkString(File.pathSeparator) val sourceNames = sourceFiles.map(base).mkString(File.pathSeparator) From c75c0c804b90e789d8f001fe4e29f237ed29be58 Mon Sep 17 00:00:00 2001 From: lostflydev Date: Thu, 4 Jun 2026 19:29:34 +0500 Subject: [PATCH 9/9] Wasm: always inject exnref, rename to JSRuntime, test Deno/Bun on CI, add guide - Rename WasmRuntime -> JSRuntime and move --js-wasm-runtime into the JS option group - Always pass --experimental-wasm-exnref on Node in Wasm mode instead of gating on the Node version. The flag is documented as always required by the Scala.js Wasm backend and is accepted on every supported Node (22-25): required where exnref is still gated, a no-op where it's on by default. The old `<25` gate coupled to runtime versions and rested on a wrong comment (Node 24 is V8 13.6, not 12.x). Symmetric with Deno; logged. - Log implicit ES-module enabling only when --js-module-kind is unset; only warn "module-kind ignored" when the chosen kind isn't ES - Install Deno and Bun in all five jvm-tests CI jobs so the guarded Deno/Bun Wasm tests actually run - Add guides/advanced/scala-wasm.md --- .github/workflows/ci.yml | 20 ++++ .../scala/scala/build/internal/Runner.scala | 53 ++++------- .../scala/scala/cli/commands/run/Run.scala | 21 +++-- .../cli/commands/shared/ScalaJsOptions.scala | 2 +- .../cli/commands/shared/SharedOptions.scala | 14 +-- .../errors/UnrecognizedJSRuntimeError.scala | 4 + .../errors/UnrecognizedWasmRuntimeError.scala | 4 - .../build/preprocessing/directives/Wasm.scala | 14 +-- .../scala/scala/build/options/JSRuntime.scala | 32 +++++++ .../scala/build/options/ScalaJsOptions.scala | 9 +- .../scala/build/options/WasmRuntime.scala | 38 -------- website/docs/guides/advanced/scala-wasm.md | 91 +++++++++++++++++++ 12 files changed, 197 insertions(+), 105 deletions(-) create mode 100644 modules/core/src/main/scala/scala/build/errors/UnrecognizedJSRuntimeError.scala delete mode 100644 modules/core/src/main/scala/scala/build/errors/UnrecognizedWasmRuntimeError.scala create mode 100644 modules/options/src/main/scala/scala/build/options/JSRuntime.scala delete mode 100644 modules/options/src/main/scala/scala/build/options/WasmRuntime.scala create mode 100644 website/docs/guides/advanced/scala-wasm.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 578a2acbe5..37f47a7c47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -202,6 +202,10 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 24 + - uses: denoland/setup-deno@v2 + with: + deno-version: v2.x + - uses: oven-sh/setup-bun@v2 - name: JVM integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.jvm @@ -243,6 +247,10 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 24 + - uses: denoland/setup-deno@v2 + with: + deno-version: v2.x + - uses: oven-sh/setup-bun@v2 - name: JVM integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.jvm @@ -284,6 +292,10 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 24 + - uses: denoland/setup-deno@v2 + with: + deno-version: v2.x + - uses: oven-sh/setup-bun@v2 - name: JVM integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.jvm @@ -325,6 +337,10 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 24 + - uses: denoland/setup-deno@v2 + with: + deno-version: v2.x + - uses: oven-sh/setup-bun@v2 - name: JVM integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.jvm @@ -366,6 +382,10 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 24 + - uses: denoland/setup-deno@v2 + with: + deno-version: v2.x + - uses: oven-sh/setup-bun@v2 - name: JVM integration tests if: env.SHOULD_RUN == 'true' run: ./mill -i integration.test.jvm diff --git a/modules/build/src/main/scala/scala/build/internal/Runner.scala b/modules/build/src/main/scala/scala/build/internal/Runner.scala index b524ac705f..a87930479b 100644 --- a/modules/build/src/main/scala/scala/build/internal/Runner.scala +++ b/modules/build/src/main/scala/scala/build/internal/Runner.scala @@ -189,36 +189,16 @@ object Runner { run(command, logger, cwd = cwd, extraEnv = extraEnv) } - // Detects the major version of Node.js on PATH; cached for the JVM lifetime (lazy val). - // Returns None if node is not found or version cannot be parsed. - private lazy val nodeMajorVersion: Option[Int] = - try { - val process = new ProcessBuilder("node", "--version") - .redirectErrorStream(true) - .start() - val output = new String(process.getInputStream.readAllBytes()).trim - process.waitFor() - // Node version format: "v22.5.0" -> extract 22 - if (output.startsWith("v")) - output.drop(1).takeWhile(_.isDigit) match { - case s if s.nonEmpty => Some(s.toInt) - case _ => None - } - else None - } - catch { - case _: Exception => None - } - - // Pre-V8 13.x runtimes need --experimental-wasm-exnref for the Scala.js Wasm exception model. - // V8 13.x ships in Node 25+ (Node 24 is still on V8 12.x where exnref is gated behind the flag). - // In Node 26+, the flag may be removed from the CLI. Only pass it when Node < 25. - // None.forall(_ < 25) == true — safe fallback when version detection fails. - private def nodeNeedsWasmFlag: Boolean = nodeMajorVersion.forall(_ < 25) - - // Deno 2.x bundles V8 12.x where wasm-exnref is gated behind a flag; symmetrical reasoning to Node. - // We always set DENO_V8_FLAGS=--experimental-wasm-exnref on Wasm output until V8 13.x lands in Deno. - private def denoNeedsWasmFlag: Boolean = true + // The Scala.js Wasm backend needs --experimental-wasm-exnref for its exception model. We inject it + // implicitly in Wasm mode (and log it) on every JS runtime that hosts the output (Node.js, Deno), + // rather than detecting runtime versions. The Scala.js docs list it as always required + // (https://www.scala-js.org/doc/project/webassembly.html), and it is accepted on every supported + // version: required where exnref is still gated (e.g. Node.js 22-24) and a harmless no-op where it + // is already on by default (Node.js 25+). Always passing it avoids tracking the exact version where + // the default flips. WasmGC and typed function-references are on by default on supported runtimes; + // optional flags (--experimental-wasm-jspi for js.async/js.await, --experimental-wasm-imported-strings + // for performance) are left to the user via NODE_OPTIONS / DENO_V8_FLAGS. + private val wasmExnrefFlag = "--experimental-wasm-exnref" private def endsWithCaseInsensitive(s: String, suffix: String): Boolean = s.length >= suffix.length && @@ -257,7 +237,7 @@ object Runner { ): Seq[String] = { val nodePath = findInPath("node").fold("node")(_.toString) - val nodeFlags = if (emitWasm && nodeNeedsWasmFlag) List("--experimental-wasm-exnref") else Nil + val nodeFlags = if (emitWasm) List(wasmExnrefFlag) else Nil val command = Seq(nodePath) ++ nodeFlags ++ Seq(entrypoint.getAbsolutePath) ++ args if (jsDom) @@ -282,10 +262,10 @@ object Runner { value(findInPath("node") .map(_.toString) .toRight(NodeNotFoundError())) - val nodeFlags = if (emitWasm && nodeNeedsWasmFlag) List("--experimental-wasm-exnref") else Nil - if (emitWasm && nodeFlags.nonEmpty) + val nodeFlags = if (emitWasm) List(wasmExnrefFlag) else Nil + if (nodeFlags.nonEmpty) logger.log( - s"Wasm: adding ${nodeFlags.mkString(" ")} (required for Wasm exception handling on Node.js < 25)" + s"Wasm: adding ${nodeFlags.mkString(" ")} to Node.js (required by the Scala.js Wasm backend for exception handling)" ) if !jsDom && allowExecve && Execve.available() then { val command = Seq(nodePath) ++ nodeFlags ++ Seq(entrypoint.getAbsolutePath) ++ args @@ -367,12 +347,11 @@ object Runner { .map(_.toString) .toRight(DenoNotFoundError())) val denoFlags = Seq("run", "--allow-read") - val wasmFlag = "--experimental-wasm-exnref" val extraEnv = - if (emitWasm && denoNeedsWasmFlag) { + if (emitWasm) { // Append to any existing DENO_V8_FLAGS rather than replacing them. val existing = sys.env.get("DENO_V8_FLAGS").filter(_.nonEmpty) - val merged = existing.fold(wasmFlag)(f => s"$f $wasmFlag") + val merged = existing.fold(wasmExnrefFlag)(f => s"$f $wasmExnrefFlag") logger.log( s"Wasm: setting DENO_V8_FLAGS=$merged (required for Wasm exception handling)" ) diff --git a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala index b4ba60a090..416698a1df 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/run/Run.scala @@ -18,7 +18,7 @@ import scala.build.internal.{Constants, Runner, ScalaJsLinkerConfig} import scala.build.internals.ConsoleUtils.ScalaCliConsole import scala.build.internals.ConsoleUtils.ScalaCliConsole.warnPrefix import scala.build.internals.EnvVar -import scala.build.options.{BuildOptions, JavaOpt, PackageType, Platform, Scope, WasmRuntime} +import scala.build.options.{BuildOptions, JSRuntime, JavaOpt, PackageType, Platform, Scope} import scala.cli.CurrentParams import scala.cli.commands.package0.Package import scala.cli.commands.setupide.SetupIde @@ -478,9 +478,12 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { // Check if Wasm mode is requested if jsOpts.jsEmitWasm then { - val runtime = jsOpts.wasmRuntime + val runtime = jsOpts.jsRuntime val esModule = true // Wasm backend uses ES modules - logger.log("Wasm mode enabled: using ES module output on JS platform") + if (jsOpts.moduleKindStr.isEmpty) + logger.log( + "Wasm mode: ES module output is enabled implicitly (required by the Scala.js Wasm backend)" + ) scratchDirOpt.foreach(os.makeDir.all(_)) val jsDest = os.temp( dir = scratchDirOpt.orNull, @@ -504,17 +507,17 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { ).map { outputPath => if showCommand then runtime match { - case WasmRuntime.Deno => + case JSRuntime.Deno => Left(Runner.denoCommand(outputPath.toIO, args)) - case WasmRuntime.Node => + case JSRuntime.Node => Left(Runner.jsCommand(outputPath.toIO, args, jsDom = false, emitWasm = true)) - case WasmRuntime.Bun => + case JSRuntime.Bun => Left(Runner.bunCommand(outputPath.toIO, args)) } else { val process = value { runtime match { - case WasmRuntime.Deno => + case JSRuntime.Deno => Runner.runDeno( outputPath.toIO, args, @@ -522,7 +525,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { allowExecve = effectiveAllowExecve, emitWasm = true ) - case WasmRuntime.Node => + case JSRuntime.Node => Runner.runJs( outputPath.toIO, args, @@ -533,7 +536,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers { esModule = esModule, emitWasm = true ) - case WasmRuntime.Bun => + case JSRuntime.Bun => Runner.runBun( outputPath.toIO, args, diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/ScalaJsOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/ScalaJsOptions.scala index 2a8aae990e..ddf1237006 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/ScalaJsOptions.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/ScalaJsOptions.scala @@ -64,7 +64,7 @@ final case class ScalaJsOptions( @HelpMessage("Enable Wasm output (Scala.js Wasm backend). Uses Node.js by default. To show more options for Wasm pass `--help-wasm`") jsEmitWasm: Option[Boolean] = None, - @Group(HelpGroup.Wasm.toString) + @Group(HelpGroup.ScalaJs.toString) @Tag(tags.experimental) @HelpMessage("Wasm runtime to use: node (default), deno, bun") jsWasmRuntime: Option[String] = None, diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala index d81a8b8f48..b38ccf865b 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala @@ -251,15 +251,15 @@ final case class SharedOptions( opts: ScalaJsOptions ): Either[BuildException, options.ScalaJsOptions] = { import opts._ - val parsedWasmRuntime = jsWasmRuntime.fold( - Right(options.WasmRuntime.default): Either[BuildException, options.WasmRuntime] + val parsedJSRuntime = jsWasmRuntime.fold( + Right(options.JSRuntime.default): Either[BuildException, options.JSRuntime] ) { rt => - options.WasmRuntime.parse(rt).toRight { - val validValues = options.WasmRuntime.all.map(_.name).mkString(", ") - new scala.build.errors.UnrecognizedWasmRuntimeError(rt, validValues) + options.JSRuntime.parse(rt).toRight { + val validValues = options.JSRuntime.all.map(_.name).mkString(", ") + new scala.build.errors.UnrecognizedJSRuntimeError(rt, validValues) } } - parsedWasmRuntime.map(wasmRuntime => + parsedJSRuntime.map(jsRuntime => options.ScalaJsOptions( version = jsVersion, mode = options.ScalaJsMode(jsMode), @@ -279,7 +279,7 @@ final case class SharedOptions( remapEsModuleImportMap = jsEsModuleImportMap.filter(_.trim.nonEmpty).map(os.Path(_, Os.pwd)), jsEmitWasm = jsEmitWasm.getOrElse(false), - wasmRuntime = wasmRuntime + jsRuntime = jsRuntime ) ) } diff --git a/modules/core/src/main/scala/scala/build/errors/UnrecognizedJSRuntimeError.scala b/modules/core/src/main/scala/scala/build/errors/UnrecognizedJSRuntimeError.scala new file mode 100644 index 0000000000..fb3c8b3525 --- /dev/null +++ b/modules/core/src/main/scala/scala/build/errors/UnrecognizedJSRuntimeError.scala @@ -0,0 +1,4 @@ +package scala.build.errors + +class UnrecognizedJSRuntimeError(runtime: String, validValues: String) + extends BuildException(s"Unrecognized JS runtime: '$runtime'. Valid values: $validValues") diff --git a/modules/core/src/main/scala/scala/build/errors/UnrecognizedWasmRuntimeError.scala b/modules/core/src/main/scala/scala/build/errors/UnrecognizedWasmRuntimeError.scala deleted file mode 100644 index 2d7b01db36..0000000000 --- a/modules/core/src/main/scala/scala/build/errors/UnrecognizedWasmRuntimeError.scala +++ /dev/null @@ -1,4 +0,0 @@ -package scala.build.errors - -class UnrecognizedWasmRuntimeError(runtime: String, validValues: String) - extends BuildException(s"Unrecognized Wasm runtime: '$runtime'. Valid values: $validValues") diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala index e465f799cc..3098d635d3 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Wasm.scala @@ -2,8 +2,8 @@ package scala.build.preprocessing.directives import scala.build.Positioned import scala.build.directives.* -import scala.build.errors.{BuildException, UnrecognizedWasmRuntimeError} -import scala.build.options.{BuildOptions, Platform, ScalaJsOptions, ScalaOptions, WasmRuntime} +import scala.build.errors.{BuildException, UnrecognizedJSRuntimeError} +import scala.build.options.{BuildOptions, JSRuntime, Platform, ScalaJsOptions, ScalaOptions} import scala.cli.commands.SpecificationLevel @DirectiveGroupName("Wasm options") @@ -29,10 +29,10 @@ final case class Wasm( ) extends HasBuildOptions { def buildOptions: Either[BuildException, BuildOptions] = { val parsedRuntime = - wasmRuntime.fold(Right(WasmRuntime.default): Either[BuildException, WasmRuntime]) { rt => - WasmRuntime.parse(rt).toRight { - val validValues = WasmRuntime.all.map(_.name).mkString(", ") - new UnrecognizedWasmRuntimeError(rt, validValues) + wasmRuntime.fold(Right(JSRuntime.default): Either[BuildException, JSRuntime]) { rt => + JSRuntime.parse(rt).toRight { + val validValues = JSRuntime.all.map(_.name).mkString(", ") + new UnrecognizedJSRuntimeError(rt, validValues) } } parsedRuntime.map { runtime => @@ -46,7 +46,7 @@ final case class Wasm( ScalaOptions() BuildOptions( scalaOptions = scalaOptions, - scalaJsOptions = ScalaJsOptions(jsEmitWasm = wasmEnabled, wasmRuntime = runtime) + scalaJsOptions = ScalaJsOptions(jsEmitWasm = wasmEnabled, jsRuntime = runtime) ) } } diff --git a/modules/options/src/main/scala/scala/build/options/JSRuntime.scala b/modules/options/src/main/scala/scala/build/options/JSRuntime.scala new file mode 100644 index 0000000000..ea07735591 --- /dev/null +++ b/modules/options/src/main/scala/scala/build/options/JSRuntime.scala @@ -0,0 +1,32 @@ +package scala.build.options + +import java.util.Locale + +/** JS-based runtimes for Scala.js Wasm backend execution. All embed a Wasm engine. */ +sealed abstract class JSRuntime(val name: String) + +object JSRuntime { + case object Node extends JSRuntime("node") + case object Deno extends JSRuntime("deno") + case object Bun extends JSRuntime("bun") + + val all: Seq[JSRuntime] = Seq(Node, Deno, Bun) + + def default: JSRuntime = Node + + def parse(s: String): Option[JSRuntime] = + s.trim.toLowerCase(Locale.ROOT) match { + case "node" | "nodejs" => Some(Node) + case "deno" => Some(Deno) + case "bun" => Some(Bun) + case _ => None + } + + implicit val hashedType: HashedType[JSRuntime] = runtime => runtime.name + + implicit val hasHashData: HasHashData[JSRuntime] = HasHashData.asIs + + implicit val monoid: ConfigMonoid[JSRuntime] = ConfigMonoid.instance[JSRuntime](default) { + (a, b) => if (b == default) a else b + } +} diff --git a/modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala b/modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala index f162de8526..cd0aef14c0 100644 --- a/modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala @@ -27,7 +27,7 @@ final case class ScalaJsOptions( esVersionStr: Option[String] = None, noOpt: Option[Boolean] = None, jsEmitWasm: Boolean = false, - wasmRuntime: WasmRuntime = WasmRuntime.default + jsRuntime: JSRuntime = JSRuntime.default ) { def fullOpt: Either[UnrecognizedJsOptModeError, Boolean] = if (mode.isValid) @@ -155,7 +155,12 @@ final case class ScalaJsOptions( esVersion = esVersion(logger) ) - if (jsEmitWasm && moduleKindStr.isDefined) + val overridesWasmModuleKind = + moduleKindStr.exists { k => + val normalized = k.trim.toLowerCase(Locale.ROOT) + normalized != "es" && normalized != "esmodule" + } + if (jsEmitWasm && overridesWasmModuleKind) logger.message( s"[${Console.YELLOW}warn${Console.RESET}] Wasm mode forces ES module output; --js-module-kind is ignored" ) diff --git a/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala b/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala deleted file mode 100644 index 5afae2d49f..0000000000 --- a/modules/options/src/main/scala/scala/build/options/WasmRuntime.scala +++ /dev/null @@ -1,38 +0,0 @@ -package scala.build.options - -import java.util.Locale - -/** Represents available WebAssembly runtimes for execution. - * - * JS-based runtimes (work now with Scala.js Wasm backend): - * - Node: Uses Node.js (V8 engine) with JavaScript loader - * - Deno: Uses Deno (V8 engine) with ES module support - * - Bun: Uses Bun (JavaScriptCore engine) with ES module support - */ -sealed abstract class WasmRuntime(val name: String) - -object WasmRuntime { - case object Node extends WasmRuntime("node") - case object Deno extends WasmRuntime("deno") - case object Bun extends WasmRuntime("bun") - - val all: Seq[WasmRuntime] = Seq(Node, Deno, Bun) - - def default: WasmRuntime = Node - - def parse(s: String): Option[WasmRuntime] = - s.trim.toLowerCase(Locale.ROOT) match { - case "node" | "nodejs" => Some(Node) - case "deno" => Some(Deno) - case "bun" => Some(Bun) - case _ => None - } - - implicit val hashedType: HashedType[WasmRuntime] = runtime => runtime.name - - implicit val hasHashData: HasHashData[WasmRuntime] = HasHashData.asIs - - implicit val monoid: ConfigMonoid[WasmRuntime] = ConfigMonoid.instance[WasmRuntime](default) { - (a, b) => if (b == default) a else b - } -} diff --git a/website/docs/guides/advanced/scala-wasm.md b/website/docs/guides/advanced/scala-wasm.md new file mode 100644 index 0000000000..ccc97ec8e4 --- /dev/null +++ b/website/docs/guides/advanced/scala-wasm.md @@ -0,0 +1,91 @@ +--- +title: Scala.js with Wasm +sidebar_position: 41 +--- + +:::caution +Wasm support is **experimental**. Flags, directives and behavior may change. +::: + +Scala CLI can compile and run Scala.js sources through the experimental +[Scala.js WebAssembly backend](https://www.scala-js.org/doc/project/webassembly.html). +The backend compiles your code to a `.wasm` binary (plus a small `.js` loader) that runs on +JavaScript hosts embedding a recent V8 (Node.js, Deno) or JavaScriptCore (Bun) engine. + +The backend needs an engine with Wasm 3.0, WasmGC, the `exnref`-based exception-handling proposal, +and ES module support. Per the Scala.js docs, that means **Node.js 23+**, Chrome 137+, Firefox 131+ +or Safari 18.4+ (Bun tracks a recent JavaScriptCore). + +## Enabling Wasm + +Pass `--js-emit-wasm`, or add the `//> using wasm` directive: + +```bash ignore +scala-cli run Hello.scala --js-emit-wasm +``` + +```scala title=Hello.scala +//> using wasm + +object Hello { + def main(args: Array[String]): Unit = + println("Hello from Wasm!") +} +``` + +Enabling Wasm implies the **JS platform** and forces **ES module** output (both required by the +Scala.js Wasm backend). Scala CLI logs these implicit choices, and reports an error if you also +request a conflicting platform, e.g. `--js-emit-wasm --platform native`. + +## Choosing a runtime + +The output runs on Node.js by default. Select another runtime with `--js-wasm-runtime` +(or the `//> using wasmRuntime` directive): + +| Runtime | Flag | Engine | +|---------|-------------------------------|-----------------| +| Node.js | `--js-wasm-runtime node` (default) | V8 | +| Deno | `--js-wasm-runtime deno` | V8 | +| Bun | `--js-wasm-runtime bun` | JavaScriptCore | + +```bash ignore +scala-cli run Hello.scala --js-emit-wasm --js-wasm-runtime deno +``` + +## Runtime setup + +The `exnref`-based exception handling the backend relies on currently sits behind a flag on +V8-based runtimes. Scala CLI passes that **always-required** flag for you and logs that it did: + +- **Node.js** (23+): Scala CLI adds `--experimental-wasm-exnref` automatically. +- **Deno** (v2.x): Scala CLI sets `DENO_V8_FLAGS=--experimental-wasm-exnref` automatically, + appending to any value you already exported. +- **Bun**: no flag is needed; it relies on JavaScriptCore's built-in support. + +`--experimental-wasm-exnref` is the only flag Scala CLI injects. WasmGC and typed +function-references are already enabled by default on supported runtimes, and Scala.js polyfills the +JS string builtins, so neither needs a flag. Other, **optional** features are left to you — pass +them via `NODE_OPTIONS` / `DENO_V8_FLAGS`, for example: + +- `--experimental-wasm-jspi` — required to use `js.async` / `js.await`. +- `--experimental-wasm-imported-strings` — improves performance. +- `--turboshaft-wasm` — improves stability on Node.js 23.x (Node.js 24+ enables it by default and + has removed the flag). + +```bash ignore +NODE_OPTIONS=--experimental-wasm-jspi scala-cli run Hello.scala --js-emit-wasm +``` + +## Limitations + +These come from the Scala.js Wasm backend itself, not from Scala CLI: + +- `@JSExport` / `@JSExportAll` are **silently ignored**. JavaScript therefore can't call exported + methods of Scala classes — including `toString()`, so converting a Scala instance to a string + *from JavaScript* (e.g. in JS string concatenation) won't work. String operations on the Scala + side are unaffected. +- A single module only (`ModuleSplitStyle.FewestModules`); features that force multiple modules + (several `@JSExportTopLevel` module names, `js.dynamicImport`) aren't supported yet. +- The output runs only on JavaScript-hosted engines (browsers, Node.js, Deno, Bun, Cloudflare + Workers). Standalone Wasm VMs such as wasmtime and WasmEdge are not yet supported — see + [scala-js/scala-js#4991](https://github.com/scala-js/scala-js/issues/4991).