Prose and Templates
Everything for turning prose into pages: Markdown in, a template around it, and two ways to reach back into the HTML afterwards.
markdown
wattle.markdown.render(path) -- read a file, return HTML
wattle.markdown.to_html(source) -- the same, from a string
Comrak, with autolinking, tables, task lists, description lists and wikilinks turned on, smart punctuation applied at parse time, and raw HTML allowed through.
Two extra passes run over the prose. 1/2 becomes a real vulgar fraction, and the suffix in 27th gets wrapped in <sup>. Both respect word boundaries, and both work on the parsed document rather than the source text, so code spans, code blocks and link destinations come through exactly as written. That last part is not a detail: run the same substitutions over the source and a link to /21st-and-main turns into /21<sup>st</sup>-and-main, which is a working link becoming a 404 in the name of typography.
hbs
wattle.hbs.render(path, data)
wattle.hbs.register_helper(name, fn)
wattle.hbs.register_partial(name, path)
wattle.hbs.render_partial(name, data)
Handlebars. Templates are parsed once and cached by path for the length of one build; watch builds from scratch each time, so editing a template takes effect on the next rebuild like anything else.
Helpers written in Lua work both inline and as blocks:
wattle.hbs.register_helper("shout", function(text)
return text:upper()
end)
wattle.hbs.register_helper("each_tag", function(tags, options)
local out = {}
for i = 1, #tags do
table.insert(out, options.fn({ tag = tags[i], index = i }))
end
return table.concat(out)
end)
A block helper receives an options table carrying fn(), inverse(), hash and data, following handlebars.js closely enough that its documentation applies. fn(table) renders the block against a context of your choosing, which is what makes an iterating helper possible. That context is pushed rather than swapped in, so ../ and @root still reach past it.
What fn and inverse Are On Loan For
They belong to the helper that was handed them, while that helper is running. Two ways to find the edge:
- Save one in a global and call it later: an error about a destructed callback.
- Call one from inside another helper nested in your own block: an error about a block already rendering.
Both are the same rule from different sides.
html
local sel = wattle.html.parse(source)
Returns a selection you can query and edit.
| Methods | find children parent attr remove remove_attr replace |
| Fields | text inner_html outer_html outer_xhtml tagname |
#sel counts the matches and sel[n] picks one, so a selection iterates like a sequence:
local headings = html:find("h2")
for i = 1, #headings do
headings[i].tagname = "h3"
end
remove() detaches and returns what it removed, which is how you lift a title out of rendered Markdown and put it somewhere the layout can use it. tagname is writable, which is how you shift heading levels so a layout owns the only h1. outer_xhtml self-closes void elements, for embedding HTML inside a feed.
A fragment stays a fragment. Parsing <p>hi</p> does not hand you back an <html> wrapper.
rewriter
wattle.rewriter.visit_paths(file, function(path)
return path:fingerprint()
end)
Walks every path in a built HTML or CSS file and calls your function with each one, replacing it with whatever you return. It covers href, src, poster, data, srcset, and CSS url().
The path handed to the callback knows where it was found, so it can resolve itself:
:fingerprint()appends a content hash.:relativize()rewrites it relative to the containing file.:resolve()gives the real path on disk.
External URLs, data:, mailto:, tel: and bare anchors are left alone.