Are you happy with your logging solution? Would you help us out by taking a 30-second survey? Click here

sbt-web

Library for building sbt plugins for the web

Subscribe to updates I use sbt-web


Statistics on sbt-web

Number of watchers on Github 328
Number of open issues 21
Average time to close an issue 19 days
Main language Scala
Average time to merge a PR 4 days
Open pull requests 9+
Closed pull requests 4+
Last commit about 2 years ago
Repo Created about 6 years ago
Repo Last Updated almost 2 years ago
Size 339 KB
Organization / Authorsbt
Contributors22
Page Updated
Do you use sbt-web? Leave a review!
View open issues (21)
View sbt-web activity
View on github
Fresh, new opensource launches 🚀🚀🚀
Trendy new open source projects in your inbox! View examples

Subscribe to our mailing list

Evaluating sbt-web for your project? Score Explanation
Commits Score (?)
Issues & PR Score (?)

sbt-web

Build Status Download

This project provides the building blocks for web oriented sbt plugins by bringing together the following concerns:

  • File directory layout conventions for resources intended to be served by a web server (otherwise known as assets)
  • Incremental execution of fine-grained operations within sbt tasks
  • Utilities for managing Akka from within sbt (sbt-web based plugins use Akka)
  • Utilities for managing WebJars including the ability to extract a WebJar's contents on to disk
  • Standardised reporting of compilation style errors

sbt-web was driven from the desire to factor out client-side web concerns from the Play Framework. However sbt-web is entirely independent of Play and can be used for any project that uses sbt as its build system.

For an overview of sbt-web: http://www.ustream.tv/recorded/42774873

Available Plugins

The following is a list of plugins we know of that are built on sbt-web:

Ideas for Plugins

Plugins in Development

  • C'mon community, get involved! Watch the talk on the anatomy of an sbt-web plugin

File Directory Layout

The following directory layout is declared by sbt-web with an indication of the associated settings:

+ src
--+ main
----+ assets .....(sourceDirectory in Assets)
------+ js
----+ public .....(resourceDirectory in Assets)
------+ css
------+ images
------+ js
--+ test
----+ assets .....(sourceDirectory in TestAssets)
------+ js
----+ public .....(resourceDirectory in TestAssets)
------+ css
------+ images
------+ js

+ target
--+ web ............(webTarget)
----+ public
------+ main .......(resourceManaged in Assets)
--------+ css
--------+ images
--------+ js
------+ test .......(resourceManaged in TestAssets)
--------+ css
--------+ images
--------+ js
----+ stage ........(stagingDirectory)

The plugin introduces the notion of assets to sbt. Assets are public resources that are intended for client-side consumption e.g. by a browser. This is also distinct from sbt's existing notion of resources as project resources are generally not made public by a web server.

In sbt, asset source files are considered the source for plugins that process them. When they are processed any resultant files go into a public directory in the classpath. By configuration, sbt-web apps serve static assets from the public directory on the classpath. For example a CoffeeScript plugin would use files from sourceDirectory in Assets and produce them to resourceManaged in Assets.

All assets whether they need processing or are static in nature, will be copied to the resourceManaged destinations.

Assets can be organized however desired within the assets directory.

One last thing regarding the public and public-test folders... any WebJar depended on by the project will be automatically extracted into these folders e.g. target/web/public/main/lib/jquery/jquery.js. In addition the public-test folder receives the contents of the public folder as well as test assets. This eases the support of test frameworks given that all files are locatable from one root.

The stage directory is product of processing the asset pipeline and results in files prepared for deployment to a web server.

Configurations

sbt holds the notion of configurations which are similar to Maven's phases. Configurations aggregate settings and tasks. Familiar configurations will be Compile and Test. sbt-web introduces two new configurations named Assets and TestAssets correlating roughly with Compile and Test, but for web assets.

Incremental Execution

The incremental task API lets tasks run more quickly when they are called more than once. The idea is to do less work when tasks are called a second time, by skipping any work that has already been done. In other words, tasks only perform the incremental work that is necessary since they were last run.

To analyse which work needs to be done, a task's work is broken up into a number of sub-operations, each of which can be run independently. Each operation takes input parameters and can read and write files. The incremental task API keeps a record of which operations have been run so that that those operations don't need to be repeated in the future.

Asset Pipeline

There are two categories of sbt-web based tasks:

  • those that operate on source files
  • those that operate on web assets

Examples of source file tasks as plugins are CoffeeScript, LESS and JSHint. Some of these take a source file and produce a target web asset e.g. CoffeeScript produces JS files. Plugins in this category are mutually exclusive to each other in terms of their function i.e. only one CoffeeScript plugin will take CoffeeScript sources and produce target JS files. In summary, source file plugins produce web assets.

Asset tasks operate on web assets directly. The assets they operate on depend on a stage in the asset pipeline. Examples of web asset tasks as plugins include RequireJs optimisation, gzip and md5 hashing.

Source file tasks can be considered to provide files for the first stage of asset pipeline processing and they will be executed often e.g. for each compilation of your project's source files. Asset pipeline tasks are generally executed at the time that you wish to prepare a distribution for deployment into, say, production.

Exporting Assets

Assets are automatically available across subproject dependencies using sbt's classpath setup. The assets are exported in the webjar format and are imported in the same way as other webjar dependencies.

So given a project dependency like this, where project A depends on project B:

lazy val a = (project in file("a"))
  .enablePlugins(SbtWeb)
  .dependsOn(b)

lazy val b = (project in file("b"))
  .enablePlugins(SbtWeb)

Assets from project B are available to project A under lib/b/.

The module name for imported assets is the same as the project module name (the normalized name of the project). This can be changed with the moduleName in Assets setting.

Test assets are also exported if a test dependency is specified. For example:

lazy val a = (project in file("a"))
  .enablePlugins(SbtWeb)
  .dependsOn(b % "compile;test->test")

Packaging and Publishing

The packaging and publishing behavior is documented for sbt-web version 1.1.0 and above.

Assets are automatically packaged and published along with project classes at the following path inside the jar: META-INF/resources/webjars/module/version/. This means that assets can be shared as external library dependencies. Simply publish the project and use as a library dependency. The assets will be extracted and available under lib/module/ in the same way as other webjar dependencies or internal dependencies.

To package all assets for production there is a packageBin in Assets task, web-assets:package in the sbt shell. This packages the result of the asset pipeline. An optional path prefix can be specified with the packagePrefix in Assets setting. For example, to have assets packaged under a public directory, as used for Play Framework distributions:

WebKeys.packagePrefix in Assets := "public/"

To automatically add the production-ready assets to classpath, the following might be useful:

(managedClasspath in Runtime) += (packageBin in Assets).value

Writing a Source File task

The following represents the minimum amount of code required in a build.sbt to create a task that operates on source files i.e. those files that are available for processing from src/main/assets. Source file tasks are resource generators in sbt terms.

val mySourceFileTask = taskKey[Seq[File]]("Some source file task")

mySourceFileTask := Nil

sourceGenerators in Assets += mySourceFileTask.taskValue

The addition of the mySourceFileTask to sourceGenerators in Assets declares the task as a resource generator and, as such, will be executed in parallel with other resource generators during the WebKeys.assets task execution. Using sbt's show command will yield the directory where all source file assets have been written to e.g.:

show web-assets:assets

Source file tasks take input, typically from sourceDirectory in Assets (and/or TestAssets) and produce a sequence of files that have been generated from that input.

The following code illustrates a more complete example where input files matching *.coffee are taken and copied to an output folder:

mySourceFileTask := {
  // translate .coffee files into .js files
  val sourceDir = (sourceDirectory in Assets).value
  val targetDir = webTarget.value / "cs-plugin"
  val sources = sourceDir ** "*.coffee"
  val mappings = sources pair relativeTo(sourceDir)
  val renamed = mappings map { case (file, path) => file -> path.replaceAll("coffee", "js") }
  val copies = renamed map { case (file, path) => file -> (resourceManaged in Assets).value / path }
  IO.copy(copies)
  copies map (_._2)
}

Using the WebKeys.assets task will perform source file tasks in parallel. If you find yourself using a source file task across many projects then consider wrapping it with an sbt plugin. Example source file plugins are sbt-jshint, sbt-coffeescript and sbt-stylus.

As a final note, if you plugin depends on node modules e.g. those that are extracted from WebJars or from NPM, then you will need to have your task depend on the node module extraction task. The following illustrates how given the Assets scope:

mySourceFileTask := Def.task {
  Nil
}.dependsOn(WebKeys.nodeModules in Assets).value

If you're wrapping the task within a plugin then you will need the Plugin's scope as opposed to the Assets scope i.e.:

mySourceFileTask := Def.task {
  Nil
}.dependsOn(WebKeys.nodeModules in Plugin).value

Writing an Asset Pipeline task

The following represents the minimum amount of code required to create a pipeline stage in a build.sbt file:

import com.typesafe.sbt.web.pipeline.Pipeline

val myPipelineTask = taskKey[Pipeline.Stage]("Some pipeline task")

myPipelineTask := identity

pipelineStages := Seq(myPipelineTask)

myPipelineTask is a function that receives a Seq[PathMapping] and produces a Seq[PathMapping]. PathMapping is a tuple of (File, String) where the first member provides the full path to a file, and the second member declares the portion of that path which is to be considered relative. For example (file("/a/b/c"), "b/c").PathMapping` types are commonly used in sbt and are useful in terms of providing access to a file and preserving information about its relative path; the latter being typically useful for copying files to a target folder where the relative portion of the path must be retained.

In the above example an identity function is used i.e. what is passed in is simply returned. The task is included within a sequence and assigned to pipelineStages i.e. the sequence represents the asset pipeline. Each stage in the asset pipeline is executed after any previous stage has completed. A stage therefore receives the product of files any previous stage as input. A stage's output then becomes the input to any subsequent stage. The first stage will always receive the output of having executed source file tasks as its input.

If you have some need for the assets produced by the pipelineStages in your development environment (during play run), then you can scope the pipelineStages to the Assets config.

pipelineStages in Assets := Seq(myPipelineTask)

To perform the asset pipeline tasks use the WebKeys.stage task. If you use sbt's show command from the console then you will see the directory that the pipeline has been written to e.g.:

show web-stage

Returning what is passed in is not particularly useful. Stages tend to add and remove files from the input as expressed in the output returned. The following expanded task simulates minifying some js files and consequently adds files to the pipeline:

myPipelineTask := { mappings: Seq[PathMapping] =>
  // pretend to combine all .js files into one .min.js file
  val targetDir = webTarget.value / "myPipelineTask" / "target"
  val (js, other) = mappings partition (_._2.endsWith(".js"))
  val minFile = targetDir / "js" / "all.min.js"
  IO.touch(minFile)
  val minMappings = Seq(minFile) pair relativeTo(targetDir)
  minMappings ++ other
}

If you find yourself commonly using a pipeline stage task across projects then you should consider wrapping it with an sbt plugin. Examples of such plugins are sbt-digest, sbt-gzip, sbt-rjs and sbt-uglify. The first two illustrate stages implemented using JVM based libraries while the latter two illustrate invoking JavaScript via js-engine.

WebDriver and js-engine

The WebDriver and js-engine projects build on sbt-web and provide a DOM oriented and DOM-less means of JavaScript execution respectively. sbt-web plugins will use one of the two of these plugins depending on their DOM requirements.

sbt-web open issues Ask a question     (View All Issues)
  • about 3 years sbt-web 1.4.0 does not work with sbt-rjs
  • about 3 years sbt-web throws NPE multi project angular 2 non play build
  • about 3 years Merging source maps
  • over 3 years sbt 1.0
  • over 3 years allow test-assets to be in the assets directory
  • over 3 years Question about handling assets in sbt
  • almost 4 years Symbolic links in multi-modules play project
  • almost 4 years Silence webjars logging during compilation
  • about 4 years Including unmanagedResources as watchSources
  • about 4 years NPM webjars expanding to directories with wrong name
  • about 4 years Fat WebJars Expand node_modules to Wrong Directories
  • over 4 years sbt-web asset output directory setup question
  • over 4 years SBT is trying to copy the application.log file, RuntimeException: Could not copy
  • over 4 years Using processed sbt-web assets in multi-module play project
  • over 4 years Deduplicators are case-sensitive on Windows
  • over 4 years publishLocal
  • over 4 years Adding production ready assets to classpath breaks auto reload of css/js etc in development
  • over 4 years Disable copying everything from assets folder to public folder
  • over 4 years Webjar locator throws IllegalStateException when adding sbt-web to the project
  • about 5 years How to use sbt-web to extract some files from a WebJars dependency to a custom output directory ?
  • over 5 years Using WebJar artifactId for directory can cause issues
  • over 5 years Unminified assets remain accessible
  • over 5 years sbt-web workflow vs. Grunt workflow
  • over 5 years it:test (IntegrationTest) does not see (does not compile?) sbt-web managed sources
  • over 5 years Add sbt-jasmine
  • over 5 years Comparing sbt-web with grunt for SPAs
  • over 5 years Add support for Grunt
sbt-web open pull requests (View All Pulls)
  • Added sbt-tslint to readme
  • Workaround for inter-project dependency tracking
  • Add tip for xsbt-web-plugin users
  • made the assets log debug instead of info
  • Add sbt-web-scalajs to README.md.
  • Add missing `main` path
  • Likely incorrect reference to unmanagedSources
  • update sbt 0.13.13. fix warnings
  • adds sbt-jasmine to readme
sbt-web questions on Stackoverflow (View All Questions)
  • Minimal sbt-web pipeline without Play
  • SBT-web incremental compilation with file dependencies
  • sbt-web asset output directory setup
  • How to use broccoli with play / sbt-web?
  • Problems with sbt-web, sbt-rjs and submodules
  • Using processed sbt-web assets in multi-module play project
  • add sbt-web resources jar to classpath of sbt-native-packager
  • Can't get sbt-web to work with npm for frontend dependencies
  • Different sbt-web pipeline task settings in development vs production?
  • How can sbt-web output be used with xsbt-web-plugin via a SBT project dependency?
  • How can scala-js integrate with sbt-web?
  • sbt-web - Source file task executed 2x at a time
  • How do I get sbt-web working in Build.scala?
  • sbt web plugin: Not a valid key: jetty-run (similar: jetty-port, jetty-context, run)
  • sbt-web-plugin: specifying classpath to jetty with configurationXml
  • how do you get sbt web plugin to work with a .scala config
  • SBT Web Plugin : Error getting ScopedKey(Scope(This,Select(ConfigKey(container)),This,This),full-classpath)
sbt-web list of languages used
Other projects in Scala