Reference
ScalaJSBundlerPlugin
The ScalaJSBundlerPlugin sbt plugin automatically enables ScalaJSPlugin on the project. It configures
the kind of output of the project to be ModuleKind.CommonJSModule. Finally, it also configures its
execution environment so that npm packages are fetched (by running the npm update command in the
project’s target directory) before the project is run or tested.
It is also possible to bundle the application and its dependencies into a single .js file by using
the webpack task scoped to a Scala.js stage (fastOptJS or fullOptJS):
The webpack task returns a list of artifacts produced by the bundling process.
JavaScript Dependencies
To define the npm packages your project depends on, use the npmDependencies key:
You can also scope dependencies to Test:
Your facades must use
@JSImportin order to work with the npm modules, otherwise you will need some additional configuration, as explained here.
Last but not least, the .js files that are in your classpath and in the jsSourceDirectories are automatically
copied to the working directory of the node command. This means that you can also @JSImport these modules from
your Scala facades (you can see an example
here).
jsdom Support for Tests
If your tests execution environment require the DOM, add the following line to your build:
Then, ScalaJSBundlerPlugin will automatically download jsdom and bundle the tests before
their execution so that they can be loaded by jsdom.
You can find an example of project requiring the DOM for its tests here.
Yarn
By default, npm is used to fetch the dependencies but you can use Yarn by setting the
useYarn key to true:
If your sbt (sub-)project directory contains a yarn.lock, it will be used. Else, a new one will be created. You should check yarn.lock into source control.
Yarn 0.22.0+ must be available on your machine.
Bundling Mode
Each time you change something in your application source code and compile the project, Scala.js emits a new .js
file that can weigh several MBs if your application is large. Scalajs-bundler provides
a few different options with respect to handling this large output file, controlled by setting the optional
webpackBundlingMode key and can be scoped to a Scala.js stage (fastOptJS or fullOptJS).
Application (default)
webpackBundlingMode := BundlingMode.Application generates a webpack config that simply processes the Scala.js output file as an
entrypoint. This means that webpack loaders, plugins, etc will work as usual. It also means that webpack will
have to process a very large Scala.Js output file.
Turning this CommonJS module into code executable by web browsers takes time, often upwards of a minute.
Nonetheless, this is identical to what you'd get if were to duplicate your workflow outside scalajs-bundler, so it remains the default.
Library Only
You can get a much faster “change source and reload application” workflow by setting the
webpackBundlingMode := BundlingMode.LibraryOnly() key. This bundling mode avoids having webpack process the entire
Scala.js output, but instead uses webpack to bundle all the javascript dependencies (determined via @JSImport
and any changes to the webpack.config.js). This is accomplished by setting the webpack output.library and
output.libraryTarget keys in the webpack.config. By default, this generates a global variable named
ScalaJSBundlerLibrary, but if needed, that variable name can be overridden via the exportedName
parameter provided to BundlingMode.LibraryOnly.
The drawback of this mode is that because the output of Scala.js is not processed by Webpack, it is still a CommonJS module that is not directly executable by web browsers. This problem is addressed by including a special “loader” before including the Scala.js module.
The inclusion of a CommonJS module in the web browser is not guaranteed to work. In particular, in
fullOptJSmode the name mangler might produce names that could clash with other existing global names. For this reason, we don’t recommend using theLibraryOnlybundling mode infullOptJS, or in production.
In order to use this mode, instead of including yourapp-bundle.js in your page, you will need to include
yourapp-library.js (which contains all the application libraries bundled into a single file),
yourapp-loader.js (which contains a hack to make the application work), and yourapp-fastopt.js or
yourapp-opt.js (which contains the output of Scala.js, that is a CommonJS module). If you're using Play
Framework, you could use a twirl template similar to this one:
The default variable global for the library is ScalaJsBundlerDependencies. Should you need to change it,
you can provide a new variable name to the configuration, such as webpackBundlingMode := BundlingMode.LibrariesOnly("myLib").
Benefits
By avoiding processing the entire Scala.js output file, webpack times from 10's of seconds to minutes to
a couple of seconds, depending on how many dependencies webpack has to resolve. In addition, since the
module references are all still managed by webpack, any custom loaders such as the html-loader or text-loader
will work as usual. Other webpack configuration such as external modules also work out of the box.
By splitting the output into a separate library and Scala.js app we are able to leverage browser caching for most reloads, since it's seldom that both .js assets and Scala.js assets change at the same time.
By avoiding any post-processing of the Scala.js output, we leave the sourcemap generated by the Scala.js compiler entirely intact. This means we avoid any translation or lookup errors introduced by webpack, and also means we save a bunch more processing time even if sourcemaps are enabled.
How It Works
Scalajs-bundler has a unique advantage in the JavaScript build ecosystem in that it runs inside SBT and has
access to the Scala.Js compiler internals. By leveraging these, we can generate a fake entrypoint.js file that
represents all the @JSImports from a Scala.JS project. We can use this entrypoint to generate a library file
that provides access to all those imports without webpack having to ever process the entire Scala.js output.
Inside the entrypoint.js file we also provide a trivial require implementation that provides access to the
modules which were imported.
All that remains is to provide an exports variable with a reference to the require implementation
for the Scala.js out to hang onto, which is what the loader provides.
Library and Application
bundlingMode := BundlingMode.LibraryAndApplication() builds on BundlingMode.LibraryOnly and attempts to
produce only one artifact (like the Application bundling mode) without the overhead processing the entire
Scala.js output file.
It uses the same library file generation process as BundlingMode.LibraryOnly. It then bundles that library,
the loader, and the Scala.js output into a yourapp-bundle file by concatenating them. If enableSourceMaps := true,
it will attempt to use the node.js concat-with-sourcemaps module to combine the sourcemaps as well.
If you need interoperability with the full Application mode or somehow can't handle the two files generated
by the LibraryOnly() mode, this mode may be useful for you. However, it relies on post-processing and eliminates
some of the benefits of the LibraryOnly mode, so consider carefully if you really need it before turning it on.
Tasks and Settings
The tasks and settings that control the plugin are documented in the API documentation of the ScalaJSBundlerPlugin.
WebScalaJSBundlerPlugin
The WebScalaJSBundlerPlugin provides integration with sbt-web-scalajs.
Enable this plugin on JVM projects that need to use .js artifacts produced by Scala.js projects (ie in places
where you used to enable WebScalaJS).
The plugin tunes the scalaJSPipeline to use the bundles produced by webpack rather than the direct
output of the Scala.js compilation.
Importing Assets from NPM Packages
Some NPM packages also contain static assets (e.g. fonts, stylesheets, images, etc.). You can make them available as sbt-web assets as follows:
Note: for older sbt versions use
(nodeModules / "font-awesome").***instead.
Where client is the identifier of an sbt project that uses the ScalaJSBundlerPlugin. The above configuration
makes all the files within the font-awesome package available as sbt-web assets.
These assets keep their path prefix relative to the node_modules directory: for instance the asset path of the
css/font-awesome.min.css resource is font-awesome/css/font-awesome.min.css.
Tasks and Settings
The tasks and settings that control the plugin are documented in the API documentation of the WebScalaJSBundlerPlugin.