Skip to content

adamkpickering/jenny

Repository files navigation

jenny

How does this work again?

- Me, every time I try to update my Hugo-based personal website

jenny is a static site generator for people who want to build their own website, but who also want to write its pages in markdown. It strives to be easy to understand so that infrequent updates aren't painful. It uses the Go templating language, and features a serve subcommand with hot reloading for convenient development. Statically linked binaries are provided for easy installation and uninstallation.

How does it work?

The core of jenny is jenny build. When you run jenny build, jenny recursively iterates over the files in input/. For each file, its action depends on the extension of the file:

  1. If the file has the .md extension, its front matter is stripped and parsed, and then the rest of the file is converted from markdown to HTML. The template referred to by the TemplateName field of the front matter is then filled using the compiled markdown and other context as input data. The result is written to the same relative path in output/, with the file extension changed to .html.

  2. If the file does not have the .md extension, it is simply copied to the same relative path in output/. This is useful for static files.

Project Structure

input/ contains .md files that get compiled to HTML to make the pages of your website. May also contain non-markdown files that you want to include in your website (for example static files). May contain nested directories.

templates/ contains template files that specify how the HTML produced from compiling markdown files is used in your website. This directory must be flat: files in nested directories are not considered. Only files with the .gotmpl extension are considered.

output/ contains your built website. Its structure mirrors the structure of input/, but with .md files renamed to .html files.

Note

The paths listed for these directories are the defaults, but you may change them using a configuration.yaml file at the root of your project.

Example Site

See example/ for an example of a jenny-based website.

Reference

Front Matter

Front matter is a concept that is copied from Hugo. It is a YAML-formatted preamble to the main markdown content that provides jenny with metadata about a page when building your site. These are the front matter fields that jenny supports:

Field Required Description
LastModified no The date the page was last modified
Published no The date the page was originially published
Title no The title of the page
TemplateName yes The name of the template used to build this page

configuration.yaml

configuration.yaml contains project-wide configuration. These are the fields that it supports:

Field Description
Input The path to the input directory
Output The path to the output directory
Templates The path to the templates directory

Data Available to Templates

You can see what data is available to templates for a specific content file using the jenny template-data command. Using the example site, the output of jenny template-data input/post1.md is shown below (with comments added):

# Data pertaining to the content file you passed as an argument.
Page:
    # The HTML created from the markdown content.
    Content: redacted for legibility
    # Metadata from the front matter in the content file. For specifics please
    # see the Front Matter reference.
    Metadata:
        LastModified: 2024-03-24T00:00:00Z
        Published: 2024-02-15T00:00:00Z
        TemplateName: page.gotmpl
        Title: Post One
    # The path to the built page. Useful for linking.
    Path: /post1.html
    # The unmodified markdown content.
    RawContent: redacted for legibility
    # The path to the content file.
    SourcePath: input/post1.md

# Data for all the pages in the site. Elements are the same as the Page key.
Pages:
    - Content: redacted for legibility
      Metadata:
        TemplateName: index.gotmpl
      Path: /index.html
      RawContent: redacted for legibility
      SourcePath: input/index.md
    - Content: redacted for legibility
      Metadata:
        LastModified: 2024-03-24T00:00:00Z
        Published: 2024-02-15T00:00:00Z
        TemplateName: page.gotmpl
        Title: Post One
      Path: /post1.html
      RawContent: redacted for legibility
      SourcePath: input/post1.md
    - Content: redacted for legibility
      Metadata:
        LastModified: 2024-04-25T00:00:00Z
        Published: 2024-03-16T00:00:00Z
        TemplateName: page.gotmpl
        Title: Post Two
      Path: /post2.html
      RawContent: redacted for legibility
      SourcePath: input/post2.md

# Any values that are computed at runtime.
Computed:
    Now: 2024-11-20T15:17:24.642695264-07:00

# The contents of configuration.yaml. For specifics please see
# the configuration.yaml reference.
Config:
    Input: input
    Output: output
    Templates: templates

Installation

Binaries are available from the releases page.

Alternatively if you have a Go toolchain installed you may use go install:

go install github.com/adamkpickering/jenny@latest

Note that if you do this, jenny --version will output development, not a real version. This is because goreleaser or go build with the -ldflags argument is needed to specify the version during the build.

How does hot reloading work?

In order to make development of your site as easy as possible, jenny has a serve subcommand that rebuilds your site every time a file in input/ or templates/ is changed. Here this is referred to as "hot reloading".

jenny uses websockets for this. On startup and each time a change is detected, jenny builds the site like it would for the build subcommand, and then injects a script into each HTML file in output/. The script opens a websocket against the /websocket server endpoint and listens for messages; when a message is received, it reloads the page. The server listens on /websocket, and sends a message on this websocket each time a change is detected (but only after the rebuild is completed).

This scheme works pretty well. However, it is possible that an error causes the server to exit, which means hot reloading stops. It is easy for the user to miss this. A few ways of notifying the user or recovering gracefully have been explored, but none are satisfactory - the simplest solution is to rely on the user to notice the failure and restart jenny.

Credits

Thanks to SUSE for holding Hack Week 24, which helped to get jenny to the point where it is usable!