A library designed to provide an easy way to get a versioned asset when using sbt-digest with sbt-web.
Note that if you are using Play then you should use their router since it has support for sbt-digest.
Also make sure you use sbt-digest version 1.1.4 or later due to this bug.
This library supports Scala Versions 2.12.x
and 2.13.x
.
It only has a single dependency, typesafe config
Add the following to your build.sbt
libraryDependencies ++= Seq(
"org.mdedetrich" %% "sbt-digest-reverse-router" % "0.2.0"
)
Sbt Digest Reverse Router works with resources that are product by
sbt-web and digested by sbt-digest.
The easiest way to use Sbt Digest Reverse Router is by configuration, below is the default
application.conf
sbt-digest-reverse-router {
digest-algorithm="md5"
package-prefix="public/"
}
- The
digest-algorithm
corresponds tosbt-digest
sDigestKeys.algorithms
(if you are using multiple algorithms then pick one) - The
package-prefix
corresponds tosbt-web
'sWebKeys.packagePrefix in Assets := "public/"
Then for simple usage you can call the org.mdedetrich.SbtDigestReverseRouter.findVersionedAsset(assetFileName: String)
if you have an asset in the root path or otherwise if the asset is located within a folder/s you can use
org.mdedetrich.SbtDigestReverseRouter.findVersionedAsset(path: String, assetFileName: String)
.
As an example, if we want to locate a digested version of client
's .js
file generated by
scala.js and sbt-web-scalajs
we would simply do
scalajs.html.scripts("client",
name => {
val versioned = org.mdedetrich.SbtDigestReverseRouter.findVersionedAsset(name)
s"/assets/$versioned"
},
name => getClass.getResource(s"/public/$name") != null
)
If we need to find the digested version of an asset, such as a main.css
located in the assets/stylesheets
folder,
you would simply do
<link rel="stylesheet" media="screen" href = @(s"/assets/${org.mdedetrich.SbtDigestReverseRouter.findVersionedAsset("stylesheets/","main.css")}")>
If you are using twirl
or
link(rel := "stylesheet",
media := "screen",
href := s"/assets/${org.mdedetrich.SbtDigestReverseRouter.findVersionedAsset("stylesheets/","main.css")}"
)
If you are using scalatags.
Note that sbt digest reverse router just uses pure Scala functions, so its not tied to any html templating framework (as long as your html templating framework can call Scala it should work fine)
Under the scenes when you call one of the org.mdedetrich.SbtDigestReverseRouter.findVersionedAsset
functions, sbt-digest-reverse-router will do the following
- Find sbt-digest's generated
.algorithm
file, i.e. if you havemain.css
file sbt-digest will generate amain.css.md5
if you are usingmd5
as an algorithm. This file contains the checksum of the originalmain.css
file. - If this file doesn't exist (should exist by default) then it will try and read the
contents of the original asset file
main.css
to generate the checksum. - We then simply output the versioned output of the file and we cache the result of the checksum, i.e. if the
checksum of the
main.css
file is9c119465f44ff0e091e7b1e2423d715b
, sbt-digest-reverse-router would return9c119465f44ff0e091e7b1e2423d715b-main.css
It is also recommended to aggressively cache the contents of the routes that serve your versioned assets
i.e. you should set up a separate route specifically for your versioned assets (i.e. /versioned-assets/:file
)
and you should set the cache control header to something like Cache-Control: max-age=31556926
so that requests such as
/versioned-assets/9c119465f44ff0e091e7b1e2423d715b-main.css
are cached in the browser for a long time. Alternately
you can also use a CDN for such assets.
sbt-digest-reverse-router will cache results with a memoized/cached with a java.util.concurrent.ConcurrentHashMap
to
improve on performance (the checksum of a specific file shouldn't change while your webserver is running). In general
the code is also designed to be performant rather than being idiomatic Scala code (since this can be a hotspot in many
cases). This means we use java libraries such as ByteArrayOutputStream
and InputStream
and we do manual null checks
in places where it makes sense to do so.
sbt-digest-reverse-router will throw a org.mdedetrich.SbtDigestReverseRouter.UnknownDigestAlgorithm
exception if you are using an algorithm that is not md5
or sha1
(these are the only algorithms which
sbt-digest currently supports). It will also throw a org.mdedetrich.SbtDigestReverseRouter.ResourceNotFound
exception if its unable to find the resource asset (please see look at sbt-web's
documentation to see where it places its assets).
Also note that while sbt-digest
's DigestKeys.algorithms
supports a list of algorithms, by default
sbt-digest-reverse-router only works with a single algorithm. This is both to keep the code simple as well as the fact
that in almost every situation you would only ever look up a digest with a single algorithm that you know upfront.