Static website generation made simple. A powerful, fast, clojure html templating solution.
Bootleg is a command line tool that rapidly renders clojure based templates. With inbuilt support for html, hiccup, hickory, selmer, mustache, markdown, enlive, json, yaml and edn, it enables you to pull together disparate elements and glue them together to generate the static website of your dreams.
Install for Linux:
$ curl -LO https://github.com/retrogradeorbit/bootleg/releases/download/v0.1.9/bootleg-0.1.9-linux-amd64.tgz
$ tar xvf bootleg-0.1.9-linux-amd64.tgz
$ mv bootleg ~/bin
Install for MacOS:
$ curl -LO https://github.com/retrogradeorbit/bootleg/releases/download/v0.1.9/bootleg-0.1.9-macos-amd64.zip
$ unzip bootleg-0.1.9-macos-amd64.zip
$ mv bootleg /usr/local/bin
Clone this repository and change into the examples/quickstart
directory:
$ git clone https://github.com/retrogradeorbit/bootleg.git
$ cd bootleg/examples/quickstart
A simple page:
$ cat example-simple.clj
[:html
[:body
[:h1 "A simple webpage"]
[:p "Made with bootleg for maximum powers!"]]]
$ bootleg example-simple.clj
<html><body><h1>A simple webpage</h1><p>Made with bootleg for maximum powers!</p></body></html>
A dynamic example:
$ cat example-dynamic.clj
[:div.countdown
(for [n (range 10 0 -1)]
[:p n])
[:p "blast off!"]]
$ bootleg example-dynamic.clj
<div class="countdown"><p>10</p><p>9</p><p>8</p><p>7</p><p>6</p><p>5
1E0A
</p><p>4</p><p>3</p><p>2</p><p>1</p><p>blast off!</p></div>
Mustache:
$ cat example-mustache.clj
(mustache "quickstart.html" (yaml "fields.yml"))
$ cat quickstart.html
<h1>{{ title }}</h1>
<h2>by {{ author }}</h2>
<div>{{& body }}</div>
$ cat fields.yml
title: Bootleg
author: Crispin
body: I'm going to rewrite all my sites with this!
$ bootleg example-mustache.clj
<h1>Bootleg</h1>
<h2>by Crispin</h2>
<div>I'm going to rewrite all my sites with this!</div>
Markdown support. Evaluate from command line. Easy downloading of resources by url (for any command):
$ bootleg -e '(markdown "https://raw.githubusercontent.com/retrogradeorbit/bootleg/master/README.md")'
<h1>bootleg</h1><p>Static website generation made simple. A powerful, fast, clojure templating solution that rocks!...
CSS selector based processing. The magic of enlive:
$ cat example-enlive.clj
(-> (markdown "simple.md")
(enlive/at [:p] (enlive/set-attr :style "color:green;")))
$ cat simple.md
# Markdown support
This is some simple markdown
$ bootleg example-enlive.clj
<h1>Markdown support</h1><p style="color:green;">This is some simple markdown</p>
Combine hiccup, mustache, markdown and enlive processing:
$ cat example-combine.clj
(mustache "quickstart.ht
F438
ml"
(assoc (yaml "fields.yml")
:body (markdown "simple.md" :html)))
$ bootleg example-combine.clj
<h1>Bootleg</h1>
<h2>by Crispin</h2>
<div><h1>Markdown support</h1><p>This is some simple markdown</p></div>
Data output with -d flag:
$ cat example-data.clj
(-> (mustache "quickstart.html"
(assoc (yaml "fields.yml")
:body (markdown "simple.md" :html)))
(convert-to :hickory-seq))
$ bootleg -d example-data.clj
({:type :element, :attrs nil, :tag :h1, :content ["Bootleg"]}
"\n"
{:type :element, :attrs nil, :tag :h2, :content ["by Crispin"]}
"\n"
{:type :element,
:attrs nil,
:tag :div,
:content [{:type :element,
:attrs nil,
:tag :h1,
:content ["Markdown support"]}
{:type :element,
:attrs nil,
:tag :p,
:content ["This is some simple markdown"]}]}
"\n")
$ bootleg example-data.clj # <- no -d flag means output will be html
<h1>Bootleg</h1>
<h2>by Crispin</h2>
<div><h1>Markdown support</h1><p>This is some simple markdown</p></div>
Bootleg is distributed for linux as a single executable file. Download the latest tarball from https://github.com/retrogradeorbit/bootleg/releases and then extract it. Once extracted, move the binary to your path. For system wide installation try /usr/local/bin
or for personal use ~/bin
$ curl -LO https://github.com/retrogradeorbit/bootleg/releases/download/v0.1.9/bootleg-0.1.9-linux-amd64.tgz
$ tar xvf bootleg-0.1.9-linux-amd64.tgz
$ mv bootleg /usr/local/bin
Windows support is experimental. Download (https://github.com/retrogradeorbit/bootleg/releases/download/v0.1.9/bootleg-0.1.9-windows-amd64.zip) and unzip the archive. Copy the bootleg.exe binary to somewhere on your path.
The jar release file is also an option if you have java installed. You can run it as follows:
$ java -jar bootleg-0.1.9.jar
Run at the command line for options:
$ bootleg -h
Static website generation made simple. A powerful, fast, clojure html templating solution.
Usage: bootleg [options] [clj-file]
Options:
-h, --help Print the command line help
-v, --version Print the version string and exit
-e, --evaluate CODE Pass in the hiccup to evaluate on the command line
-d, --data Output the rendered template as a clojure form instead of html
-o, --output FILE Write the output to the specified file instead of stdout
-t, --traceback Print the full exception traceback
-c, --colour Print outputs in colour where appropriate
--color Alias for --colour
Note: There is a known issue with this pod and bb v0.2.1. Use bb v0.2.2 or higher.
Install the binary, then launch the pod in babashka by invoking the binary:
(ns mybbscript
(:require [babashka.pods :as pods]))
(pods/load-pod "bootleg")
(require '[pod.retrogradeorbit.bootleg.utils :as utils])
(-> [:div
[:h1 "Using Bootleg From Babashka"]
[:p "This is a demo"]]
(utils/convert-to :html))
;;=> "<div><h1>Using Bootleg From Babashka</h1><p>This is a demo</p></div>"
When invoked as a pod, bootleg reveals the following namespaces to your babashka script:
- pod.retrogradeorbit.bootleg.glob
- pod.retrogradeorbit.bootleg.utils
- pod.retrogradeorbit.bootleg.markdown
- pod.retrogradeorbit.bootleg.mustache
- pod.retrogradeorbit.bootleg.selmer
- pod.retrogradeorbit.bootleg.html
- pod.retrogradeorbit.bootleg.yaml
- pod.retrogradeorbit.bootleg.json
- pod.retrogradeorbit.bootleg.edn
- pod.retrogradeorbit.bootleg.file
- pod.retrogradeorbit.bootleg.enlive
- pod.retrogradeorbit.net.cgrand.enlive-html
- pod.retrogradeorbit.hickory.select
- pod.retrogradeorbit.hickory.zip
Bootleg 0.1.9 requires babashka 0.0.98 or higher. Bootleg 0.1.9 requires babashka 0.0.99 or higher.
As a large library with lots of namespaces and functionality there may be parts that dont work correctly. Please open tickets for any such issues mentioning the problem is for the pod.
An example of testing verified working parts can be found here: https://github.com/retrogradeorbit/bootleg/blob/master/test/pod/bbpodtest.clj
bootleg
loads and evaluates the clj file specified on the command line, or evaluates the CODE
form specified in the -e flag. The code can return any of the supported data structures listed below. bootleg
will then automatically convert that format into html and write it out to standard out, or to FILE
if specified. This conversion to html step can be prevented by calling with the -d
flag. In this case the output will be a pretty formatted edn form of the output data structure.
bootleg
supports five main markup data structures. Three are more flexible. And two are limited. We will begin describing the two limited data structures and why they are limited.
Hiccup is a standard clojure DSL syntax for representing markup as nested sequences of vectors, and is represented in option flags by the keyword :hiccup
. An example of some hiccup: the html <div><p>This is an example</p></div>
is represented in hiccup as [:div [:p "This is an example"]]
.
Hiccup, as the term is used in bootleg, is refering to this vector form. It represents a single root element and its children. This means there are template fragments that cannot be represented like this. For example, the html snippet <p>one</p><p>two</p>
cannot be represented as hiccup. It is comprised of two hiccup forms. [:p "one"]
and [:p "two"]
. See hiccup-seq
below for information about this form.
Hickory is a format used to internally represent document trees in clojure for programmatic processing. In option flags it is referenced by the keyword :hickory
. It is very verbose and not suitable to write by hand. It is supported internally for passing between functions that use it. A simple example of some hickory: the html <p>one</p>
is represented in hickory as {:type :element, :attrs nil, :tag :p, :content ["one"]}
.
Both the hickory and enlive clojure projects use this format internally to represent and manipulate DOM trees.
Hickory, as the term is used in bootleg, is refering to this hashmap form. It represents a single root element and its children. This means there are template fragments that cannot be represented in hickory. For example, the html snippet <p>one</p><p>two</p>
cannot be represented as hickory. It is comprised of two hickory forms. {:type :element, :attrs nil, :tag :p, :content ["one"]}
and {:type :element, :attrs nil, :tag :p, :content ["two"]}
. See hickory-seq
below for information about this form.
Hiccup-seq is simply a clojure sequence (or vector) of hiccup forms. In option flags it is referenced by the keyword :hiccup-seq
. By wrapping multiple hiccup forms in a sequence, hiccup-seq can represent any single root element (and it's children) and any template fragment composed of sibling elements.
For example: the html snippet <p>one</p><p>two</p>
is represented in hiccup-seq as: ([:p "one"] [:p "two"])
Hickory-seq is simply a clojure sequence (or vector) of hickory forms. In option flags it is referenced by the keyword :hickory-seq
. By wrapping multiple hickory forms in a sequence, hickory-seq can represent any single root element (and it's children) and any template fragment composed of sibling elements.
For example: the html snippet <p>one</p><p>two</p>
is represented in hickory-seq as: ({:type :element, :attrs nil, :tag :p, :content ["one"]} {:type :element, :attrs nil, :tag :p, :content ["two"]})
html is represented internally as a string. This is a flexible type and can hold a root element and children, or a number of sibling elements sequentially.
xml is represented internally as a string. This type always starts with an XML header <?xml version="..."?>
You can output xml with bootleg by converting to type :xml
first:
$ bootleg -e '(convert-to [:link "foo"] :xml)'
<?xml version="1.0" encoding="UTF-8"?><link>foo</link>
The following functions are inbuilt into the clojure interpreter:
(markdown source & options)
Load the markdown from the file specified in source
and render it. source
can be a local file path (relative to the executing hiccup file location) or a URL to gather the markdown from.
Options can be used to alter the behaviour of the function. Options are a list of keywords and can be specified in any order after the source parameter. Options can be:
:data
Interpret thesource
argument as markdown data, not a file to load:hiccup-seq
Return the rendered markdown as a hiccup sequence data structure:hickory-seq
Return the rendered markdown as a hickory sequence data structure:html
Return the rendered markdown as an html string
eg.
$ bootleg -e '(markdown "# heading\nparagraph" :data)'
<h1>heading</h1><p>paragraph</p>
$ bootleg -d -e '(markdown "# heading\nparagraph" :data :hickory-seq)'
({:type :element, :attrs nil, :tag :h1, :content ["heading"]}
{:type :element, :attrs nil, :tag :p, :content ["paragraph"]})
$ bootleg -d -e '(markdown "# heading\nparagraph" :data :hiccup-seq)'
([:h1 "heading"] [:p "paragraph"])
$ bootleg -d -e '(markdown "# heading\nparagraph" :data :html)'
"<h1>heading</h1><p>paragraph</p>"
(mustache source vars & options)
Load a mustache template from the file specified in source
and render it substituting the vars from vars
. source
can be a local file path (relative to the executing hiccup file location) or a URL to gather the markdown from.
Options can be used to alter the behaviour of the function. Options are a list of keywords and can be specified in any order after the source parameter. Options can be:
:data
Interpret thesource
argument as mustache data, not a file to load:hiccup
Return the rendered mustache as hiccup:hiccup-seq
Return the rendered mustache as a hiccup sequence data structure:hickory
Return the rendered mustache as hickory:hickory-seq
Return the rendered mustache as a hickory sequence data structure:html
Return the rendered mustache as an html string
eg.
$ bootleg -e '(mustache "<p>{{var1}}</p><div>{{&var2}}</div>" {:var1 "value 1" :var2 "<p>markup</p>"} :data)'
<p>value 1</p><div><p>markup</p></div>
$ bootleg -d -e '(mustache "<p>{{var1}}</p>" {:var1 "value 1"} :data :hiccup-seq)'
([:p "value 1"])
(selmer source vars & options)
Load a selmer template from the file specified in source
and render it substituting the vars from vars
. source
can be a local file path (relative to the executing hiccup file location) or a URL to gather the markdown from.
Options can be used to alter the behaviour of the function. Options are a list of keywords and can be specified in any order after the source parameter. Options can be:
:data
Interpret thesource
argument as selmer data, not a file to load:hiccup
Return the rendered selmer as hiccup:hiccup-seq
Return the rendered selmer as a hiccup sequence data structure:hickory
Return the rendered selmer as hickory:hickory-seq
Return the rendered selmer as a hickory sequence data structure:html
Return the rendered selmer as an html string
eg.
$ bootleg -e '(selmer "<p>Hello {{name|capitalize}}!</p>" {:name "world"} :data)'
<p>Hello World!</p>
$ bootleg -d -e '(selmer "<p>Hello {{name|capitalize}}!</p>" {:name "world"} :data :hiccup-seq)'
([:p "Hello World!"])
The selmer
namespaces are also provided at their usual namespace locations.
- selmer.filter-parser
- selmer.filters
- selmer.middleware
- selmer.node
- selmer.parser
- selmer.tags
- selmer.template-parser
- selmer.util
- selmer.validator
(slurp source)
Load the contents of a file, from a local or remote source, into memory and return it. This slurp
can load from URLs. Does no interpretation of the file contents at all. Returns them as is.
(html source & options)
Loads the contents of a html or xml file and returns them in :hiccup-seq
(by default).
Options can be:
:data
Interpret thesource
argument as markdown data, not a file to load:hiccup
Return the rendered markdown as hiccup:hiccup-seq
Return the rendered markdown as a hiccup sequence data structure:hickory
Return the rendered markdown as hickory:hickory-seq
Return the rendered markdown as a hickory sequence data structure:html
Return the rendered markdown as an html string
eg.
$ bootleg -d -e '(html "<h1>heading</h1><p>body</p>" :data :hiccup-seq)'
([:h1 "heading"] [:p "body"])
$ bootleg -d -e '(html "<div><h1>heading</h1><p>body</p></div>" :data :hiccup)'
[:div [:h1 "heading"] [:p "body"]]
(hiccup source)
Loads and evaluates the clojure source from another file.
(glob pattern)
Returns a sequence of files that match the globbing pattern pattern
. Supports *
, **
, ?
, [abc]
, [a-z]
, [!a]
and relative file paths .
and ..
. File paths are returned relative to the directory of the executing file.
$ bootleg -d -e '(glob "**/*.y?l")'
(".github/workflows/deploy.yml"
".circleci/config.yml"
"examples/quickstart/fields.yml"
"test/files/simple.yml")
(symlink link target)
Make a symlink from link
to target
. Operates idempotently. If the link already exists it does nothing. If the link
exists but points to another target, changes the link to point to the specified target
. If the link
exists but is not a symbolic link, throws an exception. On success returns the path of the link.
(mkdir path)
Make a directory path
. Does not create any parent directories. Operates idempotently. If the direcotry exists, it does nothing. Otherwise it tries to create the directory. On success it returns the directory path.
(mkdirs path)
Make a directory path
including all the parent directories. Operates idempotently. If the direcotry exists, it does nothing. Otherwise it tries to create the directories. On success it returns the final directory path.
(spit filename data)
Write data
into the specified filename
. The filename is interpereted relative to the path of the current script.
(yaml source & options)
Load yaml data from source
and process it. Returns the parsed data structure.
Options can be:
:data
Interpret thesource
argument as yaml data, not a file to load
(json source & options)
Load json data from source
and process it. Returns the parsed data structure.
Options can be:
:data
Interpret thesource
argument as json data, not a file to load
(edn source & options)
Load edn data from source
and process it. Returns the parsed data structure.
Options can be:
:data
Interpret thesource
argument as edn data, not a file to load