Jinjabread
![License](https://img.shields.io/:license-mit-blue.svg)
A Python-based static site generator using Jinja templates.
Inspired by staticjinja
, jekyll
, and hugo
.
Install
pip install jinjabread
Usage
Create new site project
python -m jinjabread new mysite
Build site
python -m jinjabread build mysite
Preview site locally
python -m jinjabread serve mysite
Features
- Write pages in Markdown, HTML, or text.
- Use Jinja2 templating language in Markdown, HTML, or text.
- Supports YAML metadata in Markdown pages.
- Keep static media alongside static pages.
- Index pages (i.e.,
index.html
) can list all other pages in the same directory. - Preview your static site locally with a built-in web server.
- Prettify all generated HTML (because why not?)
File structure
Important files and directories
Name | Description | Example path |
---|
Project directory | The project root | mysite |
Content directory | Contains site content where each file becomes a site page | mysite/content |
Layouts directory | Contains page layouts that gets used by the site content | mysite/layouts |
Static directory | Contains static media that gets copied to the output directory | mysite/static |
Output directory | The complete generated site, ready to be hosted | mysite/public |
Config file | Custom site configurations in TOML format | mysite/jinjabread.toml |
Example: Site project structure
mysite/
├── content
│ ├── about.html
│ ├── index.html
│ └── posts
│ ├── index.html
│ ├── my-journey
│ │ ├── index.html
│ │ └── profile-photo.jpg
│ └── my-story.html
├── jinjabread.toml
├── layouts
│ └── markdown.html
├── public
│ ├── about.html
│ ├── index.html
│ ├── posts
│ │ ├── index.html
│ │ ├── my-journey
│ │ │ ├── index.html
│ │ │ └── profile-photo.jpg
│ │ └── my-story.html
│ └── static
│ └── style.css
└── static
└── style.css
Example: File to URL translation
File path | URL path |
---|
public/index.html | / |
public/about.html | /about |
public/posts/index.html | /posts/ |
public/posts/my-story.html | /posts/my-story |
public/posts/my-journey/index.html | /posts/my-journey/ |
public/posts/my-journey/profile-photo.jpg | /posts/my-journey/profile-photo.jpg |
public/static/style.css | /static/style.css |
Site config
Default config
content_dir = "content"
layouts_dir = "layouts"
static_dir = "static"
output_dir = "public"
prettify_html = true
[context]
[[pages]]
type = "jinjabread.MarkdownPage"
glob_pattern = "**/*.md"
layout_name = "markdown.html"
[[pages]]
type = "jinjabread.Page"
glob_pattern = "**/*"
Custom config
Change output directory
output_dir = "dist"
Add global Jinja context variables
[context]
site_name = "My site"
url_origin = "https://mysite.com"
Change Markdown layout file
[[pages]]
type = "jinjabread.MarkdownPage"
glob_pattern = "**/*.md"
layout_name = "post.html"
[[pages]]
type = "jinjabread.Page"
glob_pattern = "**/*"
Add page-specific Jinja context variables
[[pages]]
type = "jinjabread.MarkdownPage"
glob_pattern = "**/*.md"
layout_name = "markdown.html"
[[pages]]
type = "jinjabread.Page"
glob_pattern = "**/*.txt"
[pages.context]
foo = "bar"
[[pages]]
type = "jinjabread.Page"
glob_pattern = "**/*"
Pages types
Markdown pages
Markdown content supports full YAML metadata.
For example, given the following content and layout:
---
title: My blog post
author: John Smith
description: A very nice story.
keywords:
- thrilling
- must-read
---
It was a cold stormy night...
<h1>{{ title }}</h1>
<h2>{{ description }}</h2>
<h3>Written by {{ author }}</h3>
<p>{{ content }}</p>
Results in the following output:
<h1>
My blog post
</h1>
<h2>
A very nice story.
</h2>
<h3>
Written by John Smith
</h3>
<p>
<p>
It was a cold stormy night...
</p>
</p>
Context variables
Default variables
All pages have these context variables:
Name | Description | Example value |
---|
url_path | The URL path of the current page | / |
file_path | The file path of the current page, relative to the output directory | index.html |
Variable precedence
Page-specific context variables override global site context variables.
For example:
[context]
fee = "fie"
foe = "fum"
[[pages]]
type = "jinjabread.Page"
glob_pattern = "**/*.txt"
[pages.context]
foe = "foo"
[[pages]]
type = "jinjabread.Page"
glob_pattern = "**/*.html"
[pages.context]
foe = "bar"
.txt
pages will have the following extra context variables:
fee = "fie"
foe = "foo"
.html
pages have the following extra context variables:
fee = "fie"
foe = "bar"
Index pages
All index pages (i.e., index.*
) has an extra context variable named pages
which is list of dictionaries of context variables from its sibling files.
For example, given the following file structure:
mysite/content/posts/
├── index.html
├── post1.md
└── post2.md
You can list all the pages in the posts
directory using:
{% for page in pages %}
<a href="{{ page.url_path }}">
{{ page.url_path.split('/') | last | title }}
</a>
{% endfor %}
Resulting in:
<a href="/posts/post1">
Post1
</a>
<a href="/posts/post2">
Post2
</a>
Contributing
Setup
python -m venv venv && \
. venv/bin/activate && \
pip install pip pip-tools --upgrade && \
pip-sync requirements.txt
Test
python -m unittest discover .
Build
python -m build
Release
export TWINE_USERNAME='__token__'
export TWINE_PASSWORD='secret-token'
python -m twine upload dist/*