The current state of sbt-web and webjars relying on it is that they are hardly keeping up with the growth of Node.js and npm. For example, the sbt-hbs
plugin is no longer maintained, and my experience shows that it won't work with any Node.js version 8 or above. Support for sbt 1.0 is also not complete in some of these webjar-based frontent tools.
As such, unlike what the question suggests, directly spawning npm processes via sbt to build the frontend is a better solution than many.
This answer from a different question provides a sufficiently reliable way to call npm from sbt, which I will only replicate here for completeness.
buildFrontend := {
val s: TaskStreams = streams.value
val shell: Seq[String] = if (sys.props("os.name").contains("Windows")) Seq("cmd", "/c") else Seq("bash", "-c")
val npmInstall: Seq[String] = shell :+ "npm install"
val npmTest: Seq[String] = shell :+ "npm run test"
val npmLint: Seq[String] = shell :+ "npm run lint"
val npmBuild: Seq[String] = shell :+ "npm run build"
s.log.info("building frontend...")
if((npmInstall #&& npmTest #&& npmLint #&& npmBuild !) == 0) {
s.log.success("frontend build successful!")
} else {
throw new IllegalStateException("frontend build failed!")
}
}