/kagami

Static HTML templating engine and preprocessor for low-frequency blogposting.

Primary LanguageShellGNU General Public License v3.0GPL-3.0

ver gpl

kagami — static microblog processor

kagami

This is a minimalist static HTML template processor and macro preprocessor written in POSIX shell, designed for low-frequency, low-effort Web 1.0-esque blogposting.

kagami provides an extensible turing tarpit for dynamically authoring webpages from plaintext Markdown files through an easy to use templating system and macro preprocessor.

Unlike other static site authoring tools, kagami stays out of your way and doesn't enforce any particular site layout or demand more information from the user through mandatory environment variables or YAML-style frontmatter blocks at the top of all your documents, it's the easy-going static microblog processor!


Templating System

There are only 2 user-extensible templates, head.htm and tail.htm, which go before and after all your generated webpages, respectively.

Macro Preprocessor

kagami also provides a user-extensible macro preprocessor, using inline syntax that can appear anywhere in your templates or plaintext Markdown.

<!-- .kagami/head.htm -->
<link rel="stylesheet" type="text/css" href="{DOC_ROOT}/res/style.css">
<!-- .kagami/tail.htm -->
<span class="footnote">{FOOTNOTE}</span>

Macros take the form {MACRO_NAME} and correspond to an existing shell environment variable, or one you define yourself using the optional macros file that is sourced at runtime. They are evaluated and replaced from the final authored webpage.

## .kagami/macros
DOC_ROOT='/var/www'
FOOTNOTE="&copy; $(date '+%Y') <your name> -- All Rights Reserved."

From this point forward, the term "Markdown" will refer to kagami's special superset of Markdown which includes inline {MACROS} and use of inline HTML.

Usage

command line option effect
clean Recursively deletes all output files that would have been created under normal operation.
-h, --help Displays help information.
-v, --version Displays version information.

An example working directory

/ (document root)
├── .kagami/
│   ├── head.htm
│   ├── tail.htm
│   └── macros
└── .src/
    ├── index.md
    ├── about.md
    ├── projects.md
    └── blog/
        ├── index.md
        ├── 5-cute-facts-about-kagami.md
        └── 10-mindbending-kagami-techniques.md

Invoking kagami searches the current directory and all parent directories above it for an existing .kagami/ template and a .src/ directory. If found, this becomes the working directory, all operations are relative to this directory.

kagami will then recurse through the .src/ directory and convert every plaintext *.md Markdown file into a corresponding *.htm file outside of .src/, preserving the same directory structure.

Subsequent invocations will only refresh webpages that are older than their corresponding source file. Indexes are always refreshed, regaredless of file age. If any files within the .kagami/ config directory have changed, all webpages will be regenerated.

Error Handling

Because kagami doesn't get in your way, very little error handling is done, if at all. You can make use of as many or as few .kagami/ template features as you want, kagami will simply warn you of missing functionality.

The .kagami/ and .src/ directories can even be empty and kagami might warn about it but won't stop you, you won't get anything useful though.

Dynamic Indexes and Link Rewriting

Markdown files can contain metadata tags, such as creation date or time of last modification, which take the permissive form <!-- word XXXX/XX/XX --> where the date string XXXX/XX/XX can be any valid human readable date understood by GNU date. Spaces between <!-- and --> are optional.

If a particular directory has an index.md, the resulting webpage will feature a dynamic list of all other webpages in the same directory sorted by creation date appended after your content.

Omitting date information lets you exclude files from this index.

You can also manually link to other pages arbitrarily.

[link]({DOC_ROOT}/path/to/file.md)
<a href="{DOC_ROOT}/path/to/file.md">...</a>

If you link to another *.md document, it will be automatically converted to an *.htm link.

To suppress this behavior in finished webpages, you can mention or link to literal .md documents using the &period; HTML entity code.

Dynamic RSS 2.0 Feeds

A dynamically updated RSS 2.0 feed rss.xml will be generated along with your authored web pages at the root of the working directory if the optional macro SITE_HOSTNAME is defined at runtime.

Items omitted from indexing will also be excluded from appearing in the RSS feed.

SITE_TITLE and SITE_DESCRIPTION are used in the RSS <title> and <description> metadata fields and are optional but recommended. Do note that they are "required" by the RSS 2.0 spec.

  • SITE_HOSTNAME describes a URL prefix such as your domain name in the form https://sub.domain.name
  • Any prefix of arbitrary directory depth, eg. https://domain.name/sub/dir/1/2/3/4 is considered valid.
    • This is required to generate valid URLs for the RSS feed.

RSS feeds are reproducible, lastBuildDate matches the pubDate of the most recent item found. This is to avoid the issue of dirtying a git worktree simply because the lastBuildDate value changed.

Notes on feed blurbs

RSS feed item descriptions (blurbs) are truncated to the first 80 words for brevity.

While old world web publishing wisdom would tell you that the entire article contents should be included within <description>, that defeats the purpose of even having a website.

Since no modern RSS/newsreader applications expects this behavior anyway, kagami assumes that newsreader will offer to open feed items in the user's web browser for the full experience.

Table of Contents and Anchor Links

When writing structured content, you can embed a dynamically generated table of contents with navigable anchor links anywhere in your markdown using local macro {TOC}.

Given the following markdown structure, matching inline anchor links will be appended to every heading in your markdown automatically.

# hello world           <span id="hello-world"></span>
## middle               <span id="middle"></span>
### lesser point        <span id="lesser-point"></span>
# second major topic    <span id="second-major-topic"></span>
<!-- {TOC} macro will expand to the following -->
<div class="toc">
* [hello world](#hello-world)
    * [middle](#middle)
        * [lesser point](#lesser-point)
* [second major topic](#second-major-topic)
</div>

Macros

When a {MACRO} is found in processed markdown, the brackets are removed, the resulting identifier is interpreted as a shell variable $MACRO and it's contents replace the macro text in-place. If the variable is empty or unset, the macro is stripped from the final webpage.

The term macros as used by this documentation can be used interchangeably with environment variables and shell variables as they are one and the same as far as kagami is concerned.

Only the characters A-Za-z0-9_ can be used as macro identifiers.

Expanded {MACROS} can contain newlines, they will be escaped by the preprocessor and will appear in the final markup.

Global Macros

These are generated and exported at startup and do not change during runtime.

User-provided shell variables and shell scripts . (dot) sourced from .kagami/macros can extend, override or unset these at will. Subshelled scripts (including those written in languages other than shell) will have read-only access only.

built-in description
VERSION Processor name and version information.
DOC_ROOT Document root prefix, set to the working directory by default.
DATE_FUNCTION Defines a custom date function alias that takes a unix timestamp and outputs a human-readable date to stdout. A plain date function is set by default.

On modifying macros at runtime

One use case for modifying global macros is the {DOC_ROOT} macro, which expands to the working directory. Leaving this to the default setting allows you to generate web pages for local viewing without an web server, simply write all intra-site URLs with {DOC_ROOT}/path/to/file.

You can deploy your webpages for use with an web server by placing unset DOC_ROOT in your .kagami/macros, it will rewrite all your intra-site URLs starting from the root of your web server /.

User-provided Macros

These can be provided to kagami at runtime to enable specific optional features.

user-provided description
SITE_HOSTNAME (optional) Defines a site hostname prefix and is the minimum requirement to generate an RSS feed.
eg. https://sub.domain.name
SITE_TITLE, SITE_DESCRIPTION (optional) Used for populating the <title> and <description> fields in RSS feed metadata and are highly recommended.

Local Macros

These are uniquely generated from every processed file at runtime and override global and user-provided shell variables.

built-in description
TITLE Taken from first markdown <h1> heading on the page, uses page filename as fallback.
CREATED Human readable timestamp taken from earliest valid date found in metadata comment.
eg. <!--created xx/xx/xxxx-->
UPDATED Human readable timestamp taken from second earliest valid date found in metadata comment.
eg. <!--updated xx/xx/xxxx-->
TOC (optional) Anchor-linked table of contents linking to all headings found on the page.

Installation

kagami is a single shell script, you can keep it with your webpages at the document root, or you can install it to your path by running make install. On installation, if you have pandoc installed, this document will be available as a manpage accessible via man kagami.

The default install location is /usr/local, you can change this with make install PREFIX=$HOME/.local

An example template and configuration is provided so you can get started. You can run ./kagami in this directory to build a sample website.

Requirements

  • Any POSIX-compatible shell
  • GNU date — Part of GNU coreutils, required for date conversion routines
  • cmark — CommonMark Markdown to HTML processor
  • cmark-gfm (preferred)cmark with GitHub Extensions
    • Adds support for inline footnotes, tables, strikethrough and autolinking URLs.
      • kagami will fall back to standard cmark if not available.
  • pandoc (optional) — Creates an online manual page from this document if available during installation

Background

kagami (かがみ) is weeb for mirror (鏡)

kagami was written to fit a particular use case, mine.

If your needs are simple, then kagami is simple, the aim of this project is not to be a full service Wordpress or Jekyll-style blog generator. It simply automates the things I wasn't willing to do by hand.

Management of static elements such as multimedia assets, client-side scripting, stylesheets and site structure fall outside of the scope of this tool.

Several scrapped iterations of this tool were previously written as a portable makefile using a C preprocessor and later, general purpose macro preprocessor GNU m4. cpp chokes on C syntax being used incorrectly, m4 chokes on stray grave symbols and common dictionary words like divert and shift as these are m4 syntax.

I ended up implementing elements from both, along with the ability to add custom functionality as part of the kagami template without making changes to the tool itself.

Example

My personal site is generated using kagami from the kagami template and markdown sources located here.

License

GNU General Public License version 3 or later.