sbt-web-scalajs is a SBT plugin which allows you to use Scala.js along with any sbt-web server. It uses the sbt-web and scala-js plugins.
Specify the sbt version in project/build.properties (you can find the latest version here):
sbt.version=1.9.7
Add the sbt-web-scalajs and Scala.js plugins to project/plugins.sbt:
addSbtPlugin("com.vmunier" % "sbt-web-scalajs" % "1.3.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.14.0")
Lastly, put the following configuration in build.sbt:
lazy val server = project.settings(
scalaJSProjects := Seq(client),
Assets / pipelineStages := Seq(scalaJSPipeline)
).enablePlugins(SbtWeb)
lazy val client = project.enablePlugins(ScalaJSPlugin, ScalaJSWeb)
Note: make sure you use the Assets scope.
To see the plugin in action, you can run sbt new with one of these Giter8 templates:
- Play with Scala.js:
sbt new vmunier/play-scalajs.g8 - Akka HTTP with Scala.js:
sbt new vmunier/akka-http-scalajs.g8
Have a look at the releases to find out about the new features, bug fixes and how to upgrade when breaking changes were introduced.
sbt-web-scalajs looks up the scalaJSStage setting from the Scala.js projects to know whether to run fastLinkJS or fullLinkJS.
scalaJSStagesetting is set toFastOptStageby default, which means sbt-web-scalajs runsfastLinkJSby default.scalaJSStage := FullOptStagecan be set in a Scala.js project, so that sbt-web-scalajs runsfullLinkJSfor that project.Global / scalaJSStage := FullOptStagesetsFullOptStagefor all the Scala.js projects from the build.
There are two plugins: WebScalaJS and ScalaJSWeb.
WebScalaJSis automatically added to your SbtWeb project.ScalaJSWebshould be manually added to the Scala.js projects that are used by your SbtWeb project.- Scala.js projects are collected in the
scalaJSProjectssetting key of the SbtWeb project. The plugin does nothing ifscalaJSProjectsis not specified or is empty. - When compilation or testing takes place, then the
WebScalaJSplugin runs all required tasks onscalaJSProjectsprojects, copies the output to sbt-web assets and takes care of Source Maps.
Defined in WebScalaJS:
-
scalaJSProjectssetting lists the Scala.js projects whose output are used by the server. -
scalaJSPipelinetask copies the JavaScript and Source Map files produced by Scala.js to the sbt-web assets. Scala files are also copied to be used for Source Maps.More precisely,
scalaJSPipelineperforms the following tasks for each project defined in thescalaJSProjectssetting:-
If Scala.js'
scalaJSStagesetting is equal to:FastOptStage, then runpackageJSDependenciesandfastLinkJS.FullOptStage, then runpackageJSDependencies,packageMinifiedJSDependenciesandfullLinkJS.
The resulting JavaScript files are copied to the sbt-web assets, along with their corresponding source map files (.map) if they exist.
-
Read the ScalaJSWeb's
sourceMappingssetting from the project and its transitive dependencies.sourceMappingslists the directories containing Scala files to be used for Source Maps. Copy all Scala files found in these directories to the sbt-web assets.
-
Defined in ScalaJSWeb:
jsMappingstask runs Scala.jsfastLinkJS/fullLinkJSand convert output files to path mappings.jsMappingsis scoped underCompile/TestandfastLinkJS/fullLinkJS. Let's have a look at the value ofCompile/fastLinkJS/jsMappingsin SBT:
> project client
> show Compile/fastLinkJS/jsMappings
[info] * (<path>/client/target/scala-2.13/client-fastopt/main.js.map,client-fastopt/main.js.map)
[info] * (<path>/client/target/scala-2.13/client-fastopt/main.js,client-fastopt/main.js)
jsMappings calls fastLinkJS, which creates two files: main.js.map and main.js.
The files are then converted to path mappings, i.e. a tuple of a file to a relative path.
The main.js file has a client-fastopt/main.js relative path.
WebScalaJS will copy main.js to the server sbt-web assets under server/target/web/public/main/client-fastopt/main.js.
We can extend jsMappings to add the output of other Scala.js tasks. When using the sbt-jsdependencies plugin, we can update jsMappings in build.sbt as follows:
import com.typesafe.sbt.web.PathMapping
val client = project.settings(
Compile / fastLinkJS / jsMappings += toPathMapping((Compile / packageJSDependencies).value),
Compile / fullLinkJS / jsMappings += toPathMapping((Compile / packageMinifiedJSDependencies).value),
...
).enablePlugins(ScalaJSPlugin, ScalaJSWeb, JSDependenciesPlugin)
def toPathMapping(f: File): PathMapping = f -> f.getNamesourceMappingssetting lists the directories containing Scala files to be used for Source Maps. The Scala files from the Scala.js project need to be copied and packaged, so that the server can serve these files to the browser when using Source Maps. Here's an example of whatsourceMappingsreturns:
> project client
> show Compile/fastLinkJS/sourceMappings
[info] * (<path>/client/src/main/scala, scala/ae0a44)
The hash ae0a44 has been computed from the directory's canonical path using sbt.io.Hash.trimHashString(f.getCanonicalPath, 6) and is used to configure the Scala.js mapSourceURI scalac option.
When generating Source Maps, Scala.js will replace the prefix path of each Scala file with its hash value.
The hash uniquely identifies a file/directory and can be safely exposed to the users as the full file path is not disclosed.
The plugin copies the Scala files to the sbt-web assets, so that they can be served to the browser and used for Source Maps.
By default, Source Maps are enabled in both fastLinkJS and fullLinkJS.
However, Source Maps can easily be disabled in fullLinkJS by adding the following line to the Scala.js project settings:
Compile / fullLinkJS / scalaJSLinkerConfig ~= (_.withSourceMap(false))
When Source Maps are disabled, the .map files and the Scala files are not copied and do not exist in the sbt-web assets.
Note that Source Maps only get requested by the browser when the DevTools is open, so it does not hinder the performance of your website.
The plugin also watches files from the Scala.js projects.
Redefine compile to trigger scalaJSPipeline when using compile, ~compile, ~run:
Compile / compile := ((Compile / compile) dependsOn scalaJSPipeline).value
New versions are automatically published to Sonatype when creating a git tag, thanks to sbt-ci-release.