├── .gitignore ├── .nrepl-history ├── README.md ├── build.boot └── src └── dthiffault ├── boot_pages.clj └── boot_pages └── impl.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | /resources/public/js 11 | /out 12 | /.repl 13 | -------------------------------------------------------------------------------- /.nrepl-history: -------------------------------------------------------------------------------- 1 | (name :aaa/bbb.css) 2 | (ns-name :aaa/bbb.css) 3 | (name :aaa 4 | ) 5 | (name :aaa/bbb.css) 6 | (str :aaa/bbb.css) 7 | (namespace :aaa/bbb.css) 8 | (def foo :aaa/bbb.css) 9 | (str (namespace foo) (name foo)) 10 | (str foo) 11 | (subs (str foo) 1) 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # boot-pages 2 | Clojure static page generation using boot. Generate multiple pages from a description and one or more layout files. 3 | Also takes care of appending cache-busting fingerprints (hashes) to marked static assets. 4 | 5 | Include in an existing boot project by adding the following lines to build.boot 6 | ```clojure 7 | (require 8 | '[dthiffault.boot-pages :refer [pages]]) 9 | ``` 10 | 11 | Then create a file named pages.edn in your resource folder 12 | ```clojure 13 | {:defaults {:layout "default.html"} 14 | :pages [{:path "index.html" :init-fn "todoapp.run()" :title "Welcome to the site"} 15 | {:path "myotherpage.html" :init-fn "settings.run()" :title "Another page another title"}]} 16 | ``` 17 | Here :path is the target path you'd like a file created at. :init-fn will be placed within a script tag and appended to the bottom of the body tag. 18 | :title will be used for the page title. The :layout should be under resources/layouts/. All attributes can be defined in the :defaults section and overriden per page. 19 | 20 | In order to generate fingerprinted urls include something like the following in your layout: 21 | ```html 22 | 23 | ``` 24 | Everything withing ${} will be treated as an asset and replaced with a string including a hash of the filename. The file should be relative to the target directory. 25 | -------------------------------------------------------------------------------- /build.boot: -------------------------------------------------------------------------------- 1 | (set-env! 2 | :resource-paths #{"src"} 3 | :dependencies `[[org.clojure/clojure "1.6.0" :scope "provided"] 4 | [boot/core "2.0.0-rc14" :scope "provided"] 5 | [adzerk/bootlaces "0.1.11" :scope "test"]]) 6 | 7 | 8 | (require '[adzerk.bootlaces :refer :all]) 9 | 10 | (def +version+ "0.1.0") 11 | 12 | (bootlaces! +version+) 13 | 14 | (task-options! 15 | pom {:project 'dthiffault/boot-pages 16 | :version +version+ 17 | :description "Boot task to create static pages from a layout with asset hashing support" 18 | :url "https://github.com/DanThiffault/boot-pages" 19 | :scm {:url "https://github.com/DanThiffault/boot-pages"} 20 | :license {"Eclipse Public License" "http://www.eclipse.org/legal/epl-v10.html"}}) 21 | 22 | (deftask dev 23 | "Dev process" 24 | [] 25 | (comp 26 | (watch) 27 | (repl :server true) 28 | (pom) 29 | (jar) 30 | (install))) 31 | -------------------------------------------------------------------------------- /src/dthiffault/boot_pages.clj: -------------------------------------------------------------------------------- 1 | (ns dthiffault.boot-pages 2 | {:boot/export-tasks true} 3 | (:require 4 | [clojure.java.io :as io] 5 | [boot.pod :as pod] 6 | [boot.core :as core] 7 | [boot.util :as util] 8 | [boot.tmpdir :as tmpd] 9 | [clojure.edn :as edn])) 10 | 11 | (def ^:private deps 12 | '[[enlive "1.1.5"] 13 | [pandect "0.5.1"]]) 14 | 15 | (defn find-path [fileset filename] 16 | (->> fileset core/input-files (core/by-name [filename]) first tmpd/file .getPath)) 17 | 18 | (core/deftask pages 19 | "Create static html pages." 20 | [] 21 | (let [output-dir (core/temp-dir!) 22 | pf (-> (core/get-env) 23 | (update-in [:dependencies] into deps) 24 | pod/make-pod 25 | future) 26 | last-pages (atom nil)] 27 | (core/with-pre-wrap fileset 28 | (let [cfg-files (->> fileset core/input-files (core/by-name ["pages.edn"])) 29 | cfg (-> cfg-files first tmpd/file io/reader (java.io.PushbackReader.) edn/read)] 30 | (util/info "Generating pages from layouts...%s pages\n" (count (:pages cfg))) 31 | (doseq [p (:pages cfg)] 32 | (let [page-cfg (merge (:defaults cfg) p) 33 | layout-path (find-path fileset (:layout page-cfg)) 34 | file-path (:path page-cfg)] 35 | (pod/with-call-in @pf 36 | (dthiffault.boot-pages.impl/generate-page 37 | ~page-cfg 38 | ~layout-path 39 | ~(.getPath output-dir) 40 | ~file-path))))) 41 | (-> fileset 42 | (core/add-resource output-dir) 43 | core/commit!)))) 44 | 45 | -------------------------------------------------------------------------------- /src/dthiffault/boot_pages/impl.clj: -------------------------------------------------------------------------------- 1 | (ns dthiffault.boot-pages.impl 2 | (:require 3 | [net.cgrand.enlive-html :as html] 4 | [clojure.java.io :as io] 5 | [pandect.algo.sha1 :refer [sha1-file]])) 6 | 7 | (defn fingerprint [asset] 8 | (let [filename (subs (str asset) 1)] 9 | (str filename "?v=" (sha1-file (str "target/" filename))))) 10 | 11 | (defn generate-page [cfg layout-path output-dir rel-path] 12 | (let [template-fn (html/template 13 | (-> layout-path io/file html/html-resource) 14 | [{title :title init-fn :init-fn}] 15 | [:head :title] (html/content title) 16 | [:body] (html/append 17 | (html/html [:script {:type "text/javascript"} init-fn])) 18 | [html/any-node] (html/replace-vars fingerprint))] 19 | (spit (io/file output-dir rel-path) 20 | (reduce str (template-fn cfg))))) 21 | --------------------------------------------------------------------------------