Skip to content

Commit 72c23a5

Browse files
authored
Add a --js-emit-wasm option and a corresponding using directive (#3255)
* emit wasm * docs * . * fix docs * Test wasm is emitted * Remove only for CI * Following feedback * update ref doc
1 parent 3ad7ade commit 72c23a5

File tree

9 files changed

+67
-7
lines changed

9 files changed

+67
-7
lines changed

modules/cli/src/main/scala/scala/cli/commands/shared/ScalaJsOptions.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ final case class ScalaJsOptions(
5959
@HelpMessage("Enable jsdom")
6060
jsDom: Option[Boolean] = None,
6161

62+
@Group(HelpGroup.ScalaJs.toString)
63+
@Tag(tags.experimental)
64+
@HelpMessage("Emit WASM")
65+
jsEmitWasm: Option[Boolean] = None,
66+
6267
@Group(HelpGroup.ScalaJs.toString)
6368
@Tag(tags.should)
6469
@HelpMessage("A header that will be added at the top of generated .js files")

modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ final case class SharedOptions(
251251
smallModuleForPackage = jsSmallModuleForPackage,
252252
esVersionStr = jsEsVersion,
253253
noOpt = jsNoOpt,
254-
remapEsModuleImportMap = jsEsModuleImportMap.filter(_.trim.nonEmpty).map(os.Path(_, Os.pwd))
254+
remapEsModuleImportMap = jsEsModuleImportMap.filter(_.trim.nonEmpty).map(os.Path(_, Os.pwd)),
255+
jsEmitWasm = jsEmitWasm.getOrElse(false)
255256
)
256257
}
257258

modules/directives/src/main/scala/scala/build/preprocessing/directives/ScalaJs.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import scala.util.Try
4343
|`//> using jsModuleSplitStyleStr` _value_
4444
|
4545
|`//> using jsEsVersionStr` _value_
46+
|
47+
|`//> using jsEmitWasm` _true|false_
4648
|
4749
|`//> using jsEsModuleImportMap` _value_
4850
|""".stripMargin
@@ -65,7 +67,8 @@ final case class ScalaJs(
6567
jsAvoidClasses: Option[Boolean] = None,
6668
jsAvoidLetsAndConsts: Option[Boolean] = None,
6769
jsModuleSplitStyleStr: Option[String] = None,
68-
jsEsVersionStr: Option[String] = None
70+
jsEsVersionStr: Option[String] = None,
71+
jsEmitWasm: Option[Boolean] = None
6972
) extends HasBuildOptions {
7073
// format: on
7174
def buildOptions: Either[BuildException, BuildOptions] =
@@ -83,7 +86,8 @@ final case class ScalaJs(
8386
avoidLetsAndConsts = jsAvoidLetsAndConsts,
8487
moduleSplitStyleStr = jsModuleSplitStyleStr,
8588
esVersionStr = jsEsVersionStr,
86-
noOpt = jsNoOpt
89+
noOpt = jsNoOpt,
90+
jsEmitWasm = jsEmitWasm.getOrElse(false)
8791
)
8892

8993
def absFilePath(pathStr: String): Either[ImportMapNotFound, Path] =

modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,43 @@ trait RunScalaJsTestDefinitions { _: RunTestDefinitions =>
291291
}
292292
}
293293

294+
test("Emit Wasm") {
295+
val outDir = "out"
296+
297+
val inputs = TestInputs(
298+
os.rel / "run.scala" ->
299+
s"""//> using jsEmitWasm true
300+
|//> using jsModuleKind es
301+
|//> using jsModuleSplitStyleStr fewestmodules
302+
|
303+
|object Foo {
304+
| def main(args: Array[String]): Unit = {
305+
| println("Hello")
306+
| }
307+
|}
308+
|""".stripMargin
309+
)
310+
inputs.fromRoot { root =>
311+
val absOutDir = root / outDir
312+
313+
os.proc(
314+
TestUtil.cli,
315+
"--power",
316+
"package",
317+
"run.scala",
318+
"--js",
319+
"-o",
320+
absOutDir.toString(),
321+
"-f",
322+
extraOptions
323+
)
324+
.call(cwd = root).out.trim()
325+
expect(os.exists(absOutDir / "main.wasm"))
326+
327+
// TODO : Run WASM using node. Requires node 22.
328+
}
329+
}
330+
294331
test("remap imports directive") {
295332
val importmapFile = "importmap.json"
296333
val outDir = "out"

modules/options/src/main/scala/scala/build/internal/ScalaJsLinkerConfig.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ final case class ScalaJsLinkerConfig(
1111
jsHeader: Option[String] = None,
1212
prettyPrint: Boolean = false,
1313
relativizeSourceMapBase: Option[String] = None,
14-
remapEsModuleImportMap: Option[os.Path] = None
14+
remapEsModuleImportMap: Option[os.Path] = None,
15+
emitWasm: Boolean = false
1516
) {
1617
def linkerCliArgs: Seq[String] = {
1718
val moduleKindArgs = Seq("--moduleKind", moduleKind)
@@ -34,6 +35,7 @@ final case class ScalaJsLinkerConfig(
3435
val jsEsModuleImportMap = if (remapEsModuleImportMap.nonEmpty)
3536
Seq("--importmap", remapEsModuleImportMap.getOrElse(os.pwd / "importmap.json").toString)
3637
else Nil
38+
val jsEmitWasm = if (emitWasm) Seq("--emitWasm") else Nil
3739

3840
val configArgs = Seq[os.Shellable](
3941
moduleKindArgs,
@@ -45,7 +47,8 @@ final case class ScalaJsLinkerConfig(
4547
relativizeSourceMapBaseArgs,
4648
jsHeaderArg,
4749
prettyPrintArgs,
48-
jsEsModuleImportMap
50+
jsEsModuleImportMap,
51+
jsEmitWasm
4952
)
5053

5154
configArgs.flatMap(_.value)

modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ final case class ScalaJsOptions(
2525
moduleSplitStyleStr: Option[String] = None,
2626
smallModuleForPackage: List[String] = Nil,
2727
esVersionStr: Option[String] = None,
28-
noOpt: Option[Boolean] = None
28+
noOpt: Option[Boolean] = None,
29+
jsEmitWasm: Boolean = false
2930
) {
3031
def fullOpt: Either[UnrecognizedJsOptModeError, Boolean] =
3132
if (mode.isValid)
@@ -150,7 +151,8 @@ final case class ScalaJsOptions(
150151
smallModuleForPackage = smallModuleForPackage,
151152
esFeatures = esFeatures,
152153
jsHeader = header,
153-
remapEsModuleImportMap = remapEsModuleImportMap
154+
remapEsModuleImportMap = remapEsModuleImportMap,
155+
emitWasm = jsEmitWasm
154156
)
155157
}
156158
}

website/docs/reference/cli-options.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,10 @@ A file relative to the root directory containing import maps for ES module impor
13221322

13231323
Enable jsdom
13241324

1325+
### `--js-emit-wasm`
1326+
1327+
Emit WASM
1328+
13251329
### `--js-header`
13261330

13271331
A header that will be added at the top of generated .js files

website/docs/reference/directives.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,8 @@ Add Scala.js options
407407
`//> using jsModuleSplitStyleStr` _value_
408408

409409
`//> using jsEsVersionStr` _value_
410+
411+
`//> using jsEmitWasm` _true|false_
410412

411413
`//> using jsEsModuleImportMap` _value_
412414

website/docs/reference/scala-command/directives.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ Add Scala.js options
282282
`//> using jsModuleSplitStyleStr` _value_
283283

284284
`//> using jsEsVersionStr` _value_
285+
286+
`//> using jsEmitWasm` _true|false_
285287

286288
`//> using jsEsModuleImportMap` _value_
287289

0 commit comments

Comments
 (0)