finnkauski/mub
Folders and files
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Β | Β | |||
Β | Β | |||
Β | Β | |||
Β | Β | |||
Β | Β | |||
Β | Β | |||
Repository files navigation
A simple static site generator for my own purposes.
Usage:
```
mub config.json
```
Expected folder structure:
```
root
βββ config.json // configuration files
βββ content // markdown/html files
βββ include // extra files to copy over to output dir
βββ static // output dir
βββ templates // templates source
```
Example configuration:
```
{
"last_updated": DateTime<Utc> // as a string, otherwise will be set to `now`
"input": ".", // where is the required folder structure found
"output": "static", // where to put things
"render": ["index.html", "search.html", "cv.html"], // which templates to render
"search": true, // produces a `search-index.json` as part of the outputs
"site": { // site-wide metadata, mandatory but can be empty.
"title": "My title"
}
}
```
The content has a frontmatter and follows a markdown or HTML structure.
If a file has an md extension it's contents (after the frontmatter) will get
rendered to HTML. Otherwise it will be kept as is and treated as html.
The frontmatter has the following fields (* marks mandatory fields).
Example content file:
```
// * Title for the content
title: Post #1
// * Name of given bit of content
name: growth
// * Date associated with the given bit of content
date: 2025-09-01 20:50
// Whether to provide content to templates (useful for drafts, default: false)
publish: true
// Useful if you want the post to be available for templates to use
// but not rendered as a file (default: false)
bare: false
// Template to use when rendering (default: "post.html")
template: photo.html
// An include statement is a path in the include directory that will be
// traversed as a glob and turned into:
//
// [Location; len: N_Include].
//
// Multiple `include` statements are supported.
// The Location is a map of the following shape:
// {
// src: Path - Path where the file was located
// dst: Path - Path of where the file was put
// url: Path - A path ready to be used as a url
// filename: String - Name of the file without the path
// }
// So for every file found in the provided includes you will
// have one entry with the location data for that file.
// This is to support templates having the option to iterate
// through various files you want to include. Useful for
// things like simple galeries for images.
include: my-photos/project1
include: my-photos/project2
// Any unrecognised fields will be provided to templates as
// a mapping under `extra`.
description: My Description describing the post
tags: breakdown, photography
---
Something in pure markdown. [Search](https://kagi.com) for the syntax.
<div id="arbitrary-html">
<img src="/my-photos/1.jpg" />
</div>
```
Rendering:
Firstly, all templates, both content templates and general templates will
receive the configuration file serialised, hence the configuration file
schema allows for `site` as a top level site wide key value store to
allow injections of arbitrary data for all templates.
This is provided under the name `config` in the template context.
Otherwise the rendering step will provide the `Content` structure to each
individual content template under the name `data`:
```
{
// Whether any copying has to happen for this content or is it just
// virtualised and presented in the context
bare: bool,
// Whether this content should be visible at all
publish: bool,
// Location information for the content file itself
// See frontmatter docs.
location: {
src: String,
dst: String,
url: String,
filename: String,
},
// The post data itself
post: {
// see code, derived from frontmatter
metadata: Metadata
// raw text contents
raw: String
// html conversion of the content
html: String
// the text elements of the content (useful for search)
text: String
},
// Other files that are linked to this post (see frontmatter docs)
includes: [Location],
}
```
The top level templates such as `index.html` or anything else
defined under the `render` key in the configuration file will have
the following data provided to them in the under the name `data`:
```
{
content: [Content]
}
```
Example templates:
index.html
```
// 'subclassing' from a base template
{% extends "base.html" %}
// block to insert into `base` template
{%block head %}
<style>
html {
background: red;
}
</style>
{% endblock head %}
// content block to insert into `base` template
{% block content %}
<h1>Posts</h1>
<div>
// AvailableContent.content -> [Content]
{% for content in data.content | selectattr("publish") | sort(attribute="post.metadata.date") | reverse %}
<div>
<h3><a href="/{{ content.location.url }}">{{content.post.metadata.title}}</a></h3>
{% if content.post.metadata.extra.description %}<p>{{content.post.metadata.extra.description}}</p>{% endif %}
</div>
{% endfor %}
</div>
{% endblock content %}
```
a given post
```
{% extends "base.html" %}
{% block head %}
<script ... />
{% endblock head %}
{% block content %}
<h1>{{ data.post.metadata.title }}</h1>
{{ data.post.html | safe }}
{% endblock content %}
```
See above for the schema of `Content`.
Dependencies:
mub
βββ anyhow
βββ chrono
β βββ iana-time-zone
β β βββ core-foundation-sys
β βββ num-traits
β β [build-dependencies]
β β βββ autocfg
β βββ serde
β βββ serde_derive
β βββ proc-macro2
β β βββ unicode-ident
β βββ quote
β β βββ proc-macro2
β βββ syn
β βββ proc-macro2
β βββ quote
β βββ unicode-ident
βββ glob
βββ minijinja
β βββ memo-map
β βββ self_cell
β βββ serde
βββ pulldown-cmark
β βββ bitflags
β βββ getopts
β β βββ unicode-width
β βββ memchr
β βββ pulldown-cmark-escape
β βββ serde
β βββ unicase
βββ rayon
β βββ either
β βββ rayon-core
β βββ crossbeam-deque
β β βββ crossbeam-epoch
β β β βββ crossbeam-utils
β β βββ crossbeam-utils
β βββ crossbeam-utils
βββ serde
βββ serde_json
βββ itoa
βββ memchr
βββ ryu
βββ serde