# Cookbook ![](toctree:local=true,mergeFirst=true) ## How to use a custom webpack configuration file? {#custom-config} First, configure the `webpackConfigFile` setting to refer to your configuration file: ~~~ scala webpackConfigFile in fastOptJS := Some(baseDirectory.value / "my.custom.webpack.config.js") ~~~ Or, if you want to use the same configuration file for both `fastOptJS` and `fullOptJS`: ~~~ scala webpackConfigFile := Some(baseDirectory.value / "my.custom.webpack.config.js") ~~~ Then, you can write your configuration in file `my.custom.webpack.config.js`. We recommend that you reuse the configuration file generated by scalajs-bundler and extend it, rather than writing a configuration file from scratch. You can do so as follows (in file `my.custom.webpack.config.js``): ~~~ javascript var webpack = require('webpack'); module.exports = require('./scalajs.webpack.config'); // And then modify `module.exports` to extend the configuration ~~~ The key part is the `require('./scalajs.webpack.config')`. It loads the configuration file generated by scalajs-bundler so that you can tweak it. It works because your configuration file will be copied into the internal target directory, where the scalajs-bundler generates its configuration file, and where all the npm dependencies have been downloaded (so you can also `require` these dependencies). By default `webpack` task only actually launches webpack if it detects changes in settings or in the custom webpack config file. Depending on your usage scenario, you might want to monitor some other files as well (for example, if your webpack config references some additional resources). This can be achieved by using `webpackMonitoredDirectories` setting: ~~~ scala webpackMonitoredDirectories += baseDirectory.value / "my-scss" includeFilter in webpackMonitoredFiles := "*.scss" ~~~ More fine-grained control over the list of monitored files is possible by overriding the `webpackMonitoredFiles` task. You can find a working example of custom configuration file [here](https://github.com/scalacenter/scalajs-bundler/blob/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/prod.webpack.config.js). It is also possible to configure a webpack config file to be used in reload workflow and when running the tests. This configuration may not contain `entry` and `output` configuration but can be used to configure loaders etc. These configuration files are configured using `webpackConfigFile in reloadTask` or `webpackConfigFile in Test`. For example: ~~~ scala webpackConfigFile in webpackReload := Some(baseDirectory.value / "common.webpack.config.js") webpackConfigFile in Test := Some(baseDirectory.value / "common.webpack.config.js") ~~~ ## Sharing webpack configuration among configuration files {#shared-config} In addition to the configured webpack config file, all .js files in the project base directory (as configured using the `webpackResources` setting) are copied to the target directory so they can be imported from the various configuration files. Here are the steps to share the loader configuration among your prod and dev config files. This uses webpack-merge for convenience. The same result could be accomplished using plain js only. 1. Put configuration in a common.webpack.config.js file: ~~~ javascript module.exports = { module: { loaders: [ ... ], rules: [ ... ] } } ~~~ 2. Add webpack-merge to your `npmDevDependencies`: ~~~ npmDevDependencies in Compile += "webpack-merge" -> "4.1.0" ~~~ 3. Merge in the common configuration in your dev.webpack.js file: ~~~ javascript var merge = require("webpack-merge") var commonConfig = require("./common.webpack.config.js") module.exports = merge(commonConfig, { ... }) ~~~ You can find a working example of a project using a shared configuration file [here](https://github.com/scalacenter/scalajs-bundler/blob/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig). ## How to use npm modules from Scala code? {#facade} Once you have [added npm dependencies](getting-started.md) to the packages you are interested in, you have to *import* them from your code to effectively use them. The recommended way to do that is to: 1. Write a [Scala.js](https://www.scala-js.org/doc/interoperability/facade-types.html) facade annotated with [`@JSImport`](https://www.scala-js.org/doc/interoperability/facade-types.html#a-nameimporta-imports-from-other-javascript-modules) ; 2. Refer to this facade from your code. Let’s illustrate this with an example. Say that you want to write a facade for the following npm module: ~~~ javascript tab="foo.js (CommonJS)" exports.bar = function (i) { return i + 1 }; ~~~ ~~~ javascript tab="foo.js (ES6)" export const bar = i => i + 1; ~~~ The corresponding Scala.js facade looks like the following: ~~~ scala import scala.scalajs.js import scala.scalajs.js.annotation.JSImport @JSImport("foo", JSImport.Namespace) @js.native object foo extends js.Object { def bar(i: Int): Int = js.native } ~~~ There are several points worth highlighting: - The first parameter of the `@JSImport` annotation is the npm module path. This is the value you would pass to the [Nodejs `require`](https://nodejs.org/docs/latest/api/modules.html#modules_all_together) function ; - The second parameter of `@JSImport` is the name of the imported member, or like in our case, `JSImport.Namespace`, to import the whole module instead of just one particular member ; - The facade is concrete. It can either be a Scala `object` or a `class` ; - The facade has a [“JS native” type](https://www.scala-js.org/doc/interoperability/facade-types.html). > {.note} > Other styles of facades (importing a member in particular, importing functions and classes, > importing local JavaScript files, etc.) can be found in > [these tests](https://github.com/scalacenter/scalajs-bundler/blob/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/facade-examples). Finally, in your Scala code, just refer to the `foo` object: ~~~ scala object Main { def main(args: Array[String]): Unit = { println(foo.bar(42)) } } ~~~ ## How to publish a facade for an npm module? {#publish} Create a project for the facade and enable the `ScalaJSBundlerPlugin` as described [here](getting-started.md). Implement the facade as explained in the [above section](cookbook.md#facade). Publish the Scala.js project [as usual](http://www.scala-sbt.org/1.0/docs/Publishing.html). Finally, to use the facade from another Scala.js project, this one needs both to add a dependency on the facade and to enable the `ScalaJSBundlerPlugin` plugin. > {.warning} > Projects that **use** the facade also have to enable the `ScalaJSBundlerPlugin` plugin, > otherwise the dependencies of the facade will not be resolved. ## How to use an existing facade assuming the JS library to be exposed to the global namespace? {#global-namespace} Webpack is able to require external modules by using [imports-loader](https://github.com/webpack-contrib/imports-loader) and expose them to the global namespace by using [expose-loader](https://github.com/webpack/expose-loader). Thus, you can write a custom webpack configuration file that uses this loaders to expose the required modules to the global namespace. Typically, this file will look like this: ~~~ javascript src=../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/global-namespace-with-jsdom-unit-testing/common.webpack.config.js ~~~ Also, tweak your `build.sbt` to add the corresponding NPM dependencies and to use the custom webpack configuration file: ~~~ scala src=../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/global-namespace-with-jsdom-unit-testing/build.sbt#relevant-settings ~~~ You can find a fully working example [here](https://github.com/scalacenter/scalajs-bundler/blob/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/global-namespace-with-jsdom-unit-testing). ## How to bundle an application having several entry points as exports? {#several-entry-points} By default, `ScalaJSBundlerPlugin` assumes that your application only has a main class, activated through `scalaJSUseMainModuleInitializer := true`, and disregards top-level *exports*. If you have exports that need to be exposed as several entry points, this will not work. In such a case, you can use `BundlingMode.LibraryAndApplication()`. `build.sbt`: ~~~ scala src=../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/library/build.sbt#relevant-settings ~~~ Then, assuming that you defined the following library: ~~~ scala src="../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/library/src/main/scala/example/Library.scala#library-definition" ~~~ You can call its methods as follows from your JavaScript code: ~~~ scala src="../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/library/index.html#library-usage" ~~~ ## How to improve the performance of the bundling process? {#performance} You can enable the [library-only bundling mode](reference.md#bundling-mode-library-only) and disable source maps: ~~~ scala webpackBundlingMode := BundlingMode.LibraryOnly() emitSourceMaps := false ~~~ ## How to select specific files from the `BundlingMode.Library` output In [library-only bundling mode](reference.md#bundling-mode-library-only) and [library with application bundling mode](reference.md#bundling-mode-library-and-application), the `webpack` task produces multiple files. In order to determine which of these files is, for instance, the [BundlerFileType.Application](api:scalajsbundler.BundlerFileType$$Application$), you can use the `_.metadata` property of the files, like this: ~~~ scala src="../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/build.sbt#filter-files" ~~~ ## How to rebuild and reload your page on code changes? {#webpack-dev-server} `scalajs-bundler` includes a simple wrapper over webpack-dev-server to simplify your workflow. It is exposed as two stage-level tasks (`startWebpackDevServer` and `stopWebpackDevServer`). The standard work session looks like this: 1. Spawn background server process: ~~~ > fastOptJS::startWebpackDevServer ~~~ By default the server is started on port `8080`. Use `webpackDevServerPort` setting to change this. 2. Instruct SBT to rebuild on source changes: ~~~ > ~fastOptJS ~~~ 3. Now each time you change a source file, Scala.js recompiles it, and webpack-dev-server switches to the updated version. 4. Shut down the background process: ~~~ > fastOptJS::stopWebpackDevServer ~~~ Additional arguments can be passed to webpack-dev-server via `webpackDevServerExtraArgs` setting. For example, you can add the following to your `build.sbt` to make your page reload on every change: ~~~ webpackDevServerExtraArgs := Seq("--inline") ~~~ ## How to pass extra parameters to webpack `scalajs-bundler` invokes `webpack` with a configuration generated either automatically from the build or set with `webpackConfigFile`. `webpack` is then called with the following arguments: ~~~ --config ~~~ You can add extra params to the `webpack` call, for example, to increase debugging ~~~ webpackExtraArgs := Seq("--profile", "--progress", "true") ~~~ **Note** Params are passed verbatim, they are not sanitized and could produce errors when passed to webpack. In particular, don't attempt to override the `--config` param. ## How to use webpack 4 `scalajs-bundler` (version 0.12.0 onwards) supports webpack 4. To enable webpack 4, set the correct versions in `build.sbt` ~~~ scala version in webpack := "4.8.1" version in startWebpackDevServer := "3.1.4" ~~~ Additionally, you need to update any webpack plugins your config uses, to Webpack 4 compatible versions. Webpack 4 has the potential to substantially reduce your webpack compilation times (80% reductions have been observed but your mileage may vary) ## How to get and use a list of assets `scalajs-bundler` (version 0.13.0 onwards) will export a list of all assets produced by webpack. You can read that list on sbt ~~~ scala val files = (webpack in (Compile, fullOptJS)).value ~~~ You can this list e.g. with [sbt-native-packager](https://github.com/sbt/sbt-native-packager)` to add mappings as: ~~~ scala src="../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets-cookbook/build.sbt#scalajs-files" ~~~ This will add all artifacts produced by the fully optimized Scala.JS run to the 'assets' directory of the target archive. If you need to package additional libraries that have been downloaded by `scalajs-bundler`, you can do something like: ~~~ scala src="../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets-cookbook/build.sbt#additional-files" ~~~ Also, any static resources that you would like to have in the resulting archive (i.e. `index.html`), should live inside the `src/universal` directory of your project.