Muh (say: Moo!) stands for "mustached hypertext" and is the template language that runs Sissi, the static site generator. It is put into a single package so it can be re-used in other projects as well.
Muh comes in 2 flavors. Both of them live in the @sissijs/muh
package.
@sissijs/muh
includes everything. A full-fledged processTemplateFile
function that supports SmolYAML/JSON frontmatter, layout files, basic preprocessing such as (a minimal) markdown, handling css imports and resolving <html-include src="">
directives. In roughly 10KB of JavaScript.
@sissijs/muh/template
is just the template
function that implements the mustache syntax. It also provides the built-in filters for formatting dates/numbers, and built-in helper functions. The helper functions provided are small wrappers around fetch, namely fetchText()
and fetchJson()
. All that is contained in roughly 4KB of JavaScript.
Sizes are measured with minification and without compression.
TypeScript declarations and API documentation are autogenerated from JSDoc.
The syntax is {{ moo }}
where moo
can be any arbitrary JavaScript. JavaScript is evaluated in a safe context. The only globals it can access are the ones you provide, plus a few built-ins.
When the evaluated expression is a promise, it is automatically resolved (or rejected). When the evaluated expression is a function, it is automatically invoked without parameters.
Under the hood, it uses node:vm
, which makes it a node-only library (for now). It should work in Deno 2, as it provides some backwards-compatibility to node.js.
You can apply filters to your mustache expressions using the pipe notation. There are a few built-in filters:
{{ content | safe }} // dont escape html
{{ promise | async }} // resolve promises
{{ result | json }} // print json
{{ article.date | date: 'de' }} // 25.12.2024 (for first Christmas Day 2024)
{{ price | currency: 'de', 'euro' }} // 1.234,56 €
{{ fetchJson('/api/articles') | async | limit: 5 | each: templateFunction }}
{{ include('article.html', {title: 'Article Title'}) | safe }}
{{ '<' | htmlentities }} // <
{{ ' ' | urlencode }} // %20
There are a few built-in helper functions:
{{ fetchJson('https://yesno.wtf/api') | async | json }}
{{ fetchText('https://some-html.api/api/weather') | async }}
{{ include('partial.html', {title: 'some additional data'}) }}
HTML-files can use <html-include src="partial.html" title="some additional data"/>
directives.
Additional attributes are passed to the include's data.
There is a basic markdown processor for markdown files involved.
Markdown files also interprete the <html-include>
directive.
Also, CSS import directives are resolved.
All file types mentioned above can also work with the mustache syntax.
const result = await processTemplateFile(
'<h1>{{ title }}</h1>',
'index.html',
{title: 'Test'}
);
console.log(result) // <h1>Test</h1>