Liking cljdoc? Tell your friends :D

lein-buster

A Leiningen plugin to generate fingerprinted files suitable for use in browser cache-busting.

It was designed with the specific usecase of fingerprinting CLJS files that are bundled as part of an uberjar alongside other clojure code in mind. lein-buster provides either auto-run hooks that invoke it after leiningen.compile or a task to invoke it manually from a project.

Once run, lein-buster will output two types of artifacts:

  1. A fingerprinted version of your specified files in the format of <output-base>/<filepath-diff-minus-filename>/<filename>-<fingerprint>.<extension>.
  2. A manifest file in JSON format so that you can map the filename you know to the generated one.

Configuration

Buster needs to know which files you want to fingerprint and where to write a manifest to. You can supply this configuration inside a :buster map like so:

  :buster {;; A vector of files and dirs as strings or as regex patterns.
           ;; If any regex, then all file paths in project are listed once, and any regex is matched against the loaded list.
           ;; If any string or regex matches a directory then all files in that directory are "busted"
           :files ["resources/build/out/main.js"
                   "no-existent-file.txt"
                   "resources/build/images"
                   #"resources/build/stylesheets/.+\.css$"]

           ;; This part will be stripped away from the file path, and the remainder - lets call it "filepath-diff" -
           ;; is used in both manifest as well as appended to <output-base> when writing renamed files.
           ;; Defaults to <project-dir>.
           :files-base "resources/build"
           
           ;; Renamed files are written to <output-base>/<filepath-diff>.
           ;; Defaults <source-base>.
           :output-base "release"
           
           ;; Defaults to <output-base>/rev-manifest.json
           :manifest "release/rev-manifest.json"
           
           ;; If true, merges updated/new files to existing manifest file.
           ;; If false, replaces an existing manifest with an new file.
           ;; Defaults to false.
           :merge true}

If you're not feeling picky, you can stick that configuration at the project.clj's top level. Otherwise, you can put it at the top level of a relevant profile (example below).

Buster also supplies a hook to run itself post-compile with. You can add that to your project.clj like so:

:hooks [leiningen.buster]

If you're using a hook from another plugin that generates the static files you want fingerprinted (e.g. [lein-cljsbuild] lein-cljsbuild), you should make sure that hook comes before buster's:

:hooks [leiningen.cljsbuild leiningen.buster]

Usage

Add [no.terjedahl/lein-buster "0.2.0-SNAPSHOT"] to the :plugins vector of your project.clj.

You can invoke lein-buster manually from your project like so:

$ lein buster

This is probably not that useful, outside of making sure you've configured things properly. The auto-run configuration outlined above is probably closer to what you're looking for.

If you want to have lein-buster run only as part of your jar compilation (which is likely), your buster config in your project.clj can be nested under the relevant profile like so:

:profiles {:uberjar {:hooks [leiningen.buster]
                     :buster {:files ["resources/public/scripts/awesome.js"]
                              :manifest "resources/manifest.json"}}}

Integrating

You'll want to parse the generated manifest file and expose the mappings to your views so that you can substitute the file name you know (e.g. application.js) to the fingerprinted version (e.g. application-acbd18db4c.js).

In my current project, we use cheshire to parse the JSON from clojure and construct our views using selmer. We added a custom selmer tag to help with this translation:

(defn load-revision-manifest
  [path]
  (when-let [manifest (io/resource path)]
    (-> manifest slurp chesh/parse-string)))

(def revision-manifest
  (memoize load-revision-manifest))

;; Returns the fingerprinted asset name for a file listed in the revision manifest.
(selmer/add-tag! :asset-name
                 (fn [args context-map]
                   (let [asset (first args)
                         manifest (revision-manifest "manifest.json")]
                     (or (get manifest asset) asset))))

Subsequent use in a Selmer template looks like this:

<script type="text/javascript" src="/scripts/{% asset-name application.js %}"></script>

If you're using a different templating engine or a completely different approach, please add how you're integrating lein-buster to the wiki!

License

Copyright © 2016-2019 Stephen Caudill, Terje Dahl

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close