node.jssbtscala.jssbt-web

In a ScalaJs sbt build, is there any advantage to use webjars instead of npm or bower with 'Provided'?


When I first discovered webJars a few months ago I was super-skeptical that it would be be a viable way means of handling client-side dependencies given the enormous complexity of some of these builds/buildsystems, and given the frequency that js files are published. The second concern was of course not well-founded but I feel vindicated on the first after spending almost 36 hours now trying in vain to get about 10 scss/css/less-type webJars and 8 JS webJars to live under one jsDependencies roof.

What I found as that by the time you reach JS dependency 3, 4, or 5,you start getting into a ridiculous timekill loop:

1. "Oh nos! fastOptJS failed because there was some random file that was also named the same as a dependency in the webjar!"

[trace] Stack trace suppressed: run last client/compile:resolvedJSDependencies for the full output.
[error] (client/compile:resolvedJSDependencies) org.scalajs.core.tools.jsdep.JSLibResolveException: Some references to JS libraries could not be resolved:
[error] - Ambiguous reference to a JS library: bootstrap.min.js
[error]   Possible paths found on the classpath:
[error]   - META-INF/resources/webjars/bootstrap/3.3.6/js/bootstrap.min.js
[error]   - META-INF/resources/webjars/bootstrap3-dialog/1.34.4/examples/assets/bootstrap/js/bootstrap.min.js
[error]   originating from: client:compile, client:compile, client:compile, client:compile
[error] - Ambiguous reference to a JS library: bootstrap.js
[error]   Possible paths found on the classpath:
[error]   - META-INF/resources/webjars/bootstrap3-dialog/1.34.4/examples/assets/bootstrap/js/bootstrap.js
[error]   - META-INF/resources/webjars/bootstrap/3.3.6/js/bootstrap.js
[error]   originating from: client:compile, client:compile, client:compile, client:compile

2. I know what to do! I'll add a version to the defined js!

lazy val           webjarbs   =   "org.webjars"               %    "bootstrap"                       % version.bootstrap  / s"${version.bootstrap}/bootstrap.js"                      minified s"${version.bootstrap}/bootstrap.min.js"         dependsOn    "jquery.js" commonJSName  "bootstrap"

3. "Oh no! fastOptJS failed!"

[trace] Stack trace suppressed: run last client/compile:resolvedJSDependencies for the full output.
[error] (client/compile:resolvedJSDependencies) org.scalajs.core.tools.jsdep.JSLibResolveException: Some references to JS libraries could not be resolved:
[error] - Missing JS library: 3.3.6/bootstrap.js
[error]   originating from: client:compile, client:compile, client:compile, client:compile
[error] - Missing JS library: 3.3.6/bootstrap.min.js
[error]   originating from: client:compile, client:compile, client:compile, client:compile

gg boys.

This goes over and over and around and around, and then I have to start doing

lazy val         bs_sidebar   = ( "org.webjars"               %    "bootstrap-sidebar"              % version.bs_sidebar intransitive())  / "js/sidebar.js" dependsOn(s"bootstrap.js",  s"bootstrap.min.js")

and now I'm not really even using the webjar, but it has a jsdependency named X and I cannot change that...

Question

Hmmm? What if I just did what I used to do but build the dependencies without the app into some gigantic file, or set of files, and then feed that into the build? I have a proof of concept from online and I got it work (I think it was https://github.com/wav/material-ui-scalajs-react/blob/master/src/main/scala/wav/web/muiwrapper/package.scala ) which almost worked, and gave me the idea.

I know npm works a lot better than sbt, and I can still get it into my package... what's the downside, and am I missing something about sbt?


Solution

  • I agree with you. Once an application starts having non-trivial dependencies on JavaScript libraries, jsDependencies does not scale. This is mostly because WebJars are missing critical features (just as transitive dependencies), but also because jsDependencies was not a mechanism designed to scale.

    As time passed, users have asked for more and more features of jsDependencies, because they want to use it as their true app-scale (whatever that means) dependency mechanism. As a result, we've been patching more and more features/hacks on top of jsDependencies. The result is not the prettiest thing in the world, and it definitely has shortcomings.

    I would actually encourage using npm to resolve your dependencies, especially if you are familiar with it and know how to integrate it into your workflow.