I am using the scalajs-bundler plugin and have defined my build.sbt thus:
enablePlugins(ScalaJSBundlerPlugin)
name := "Reproduce"
scalaVersion := "2.12.8"
npmDependencies in Compile += "bootstrap" -> "3.4.1"
However when I run "sbt fastOptJS::webpack" there is no reference to bootstrap in the -fastopt-bundle.js file that gets generated.
Shouldn't bootstrap get included?
I just ran into the exact same issue and was not able to find the solution in any searches. Hopefully this (rather long) answer will help others avoid some pain.
I think there are 3 issues to address in order to use the Bootstrap lib bundled as an npm module (ie. using scalajs-bundler to package it via the npmDependencies parameter).
1) Get the bootstrap library into the bundle.
2) Make the jQuery symbol available to Bootstrap as a global variable.
3) Get Bootstrap loaded at run time.
1) Getting modules into the bundle
This is first issue that you mention in your question. Adding
bootstrapto npmDependencies is not sufficient to get scalajs-bundler to include your module in the bundle. In addition to that, somewhere in your scala code you must have a JSImport("library_name", ...) statement. This tells scalajs-bundler that you are actually using the library and that it needs to be included in the bundle. You can read more detail about JSImport here. I found that description a little vague. Here is an answer to a question by me from @Julien Richard-Foy that I found much more helpful. In my code the JSImport requirement is met included in part 3 below. Note, you will also need to have a JSImport for the jquery lib to make sure it gets included in the bundle also since Bootstrap depends on it, and you need to addjqueryto npmDependencies.2) Creating javascript global variables
This is the most complicated part of the solution.
In my application I would get an error in the browser console like
jQuery is not defined. It took me some time to determine this was caused inside the Bootstrap lib. The Bootstrap lib depends on the jquery lib by assuming the global variable jQuery is defined. Unfortunately, simply including jquery in the bundle via npmDependencies and JSImport are not enough. You must tell scalajs-bundler to create the jQuery global var and export it to all the modules in your bundle.The solution @Julien Richard-Foy points to is the general recipe to follow, but I believe it has a bug. The bug doesn't cause a problem in their example because the library name and the global variable name are identical. The problem is that
modNameandglobalModules[modName]are swapped on the return line of the importRule.Here is my common.webpack.config.js file for scalajs-bundler instructing it to generate a global variable
jQueryand export it. Note, the only changes I made were to placejquery: "jQuery"in globalModules and to swap positions ofmodNameandglobalModules[modName]on the return line of the importRule. Otherwise, I just followed the example (ie. other config files and changes to build.sbt are also required).Some additional resources on the operation of the imports-loader and expose-loader.
3) Loading modules at run time
Normally this is not something you have to do explicitly. However, in the case of Bootstrap (or any js library that extends another js library) you probably won't be calling the library directly through a facade. Most likely you will be using the Monkey Patching pattern. In this case a jQuery object is cast to a Bootstrap object without directly invoking the Bootstrap lib. Typically this will compile just fine, but you will get a run time error in the browser console, something like
Uncaught TypeError: jq.modal is not a function. Here is how the Bootstrap extension to jquery is defined so you can make some sense of jq and modal in the error message:The solution is to make some explicit reference to the lib sometime before the implicit is invoked.
Here's how I did it.
This is contained inside an object that contains all the wrapper definitions for Bootstrap components. This guarantees the
Bootstrap.load()gets called before any of the Bootstrap wrappers are used. I like this since there is no need to remember to call it explicitly in any of the wrapper factory methods.