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 test
ed.
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
@JSImport
in 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
fullOptJS
mode the name mangler might produce names that could clash with other existing global names. For this reason, we don’t recommend using theLibraryOnly
bundling 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 @JSImport
s 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.