source/projects/templater/templater.py

76 lines
2.1 KiB
Python

"""A tiny template(s) tool.
Processes Jekyll/Hyde/Hugo/... style 'fontmatter' headers, applying Jinja2/Liquid templating from an
optional templates and includes directory.
"""
import os
import re
import click
import jinja2
import yaml
FONTMATTER_PATTERN = re.compile(
r"^(---\n\r?(?P<fontmatter>.*?)\n\r?---\n\r?)?(?P<content>.+)$", re.DOTALL
)
@click.command()
@click.option("-i", "--include", "include_dir", multiple=True)
@click.option("-t", "--template", "template_dir", multiple=True)
@click.option("-c", "--config", "config_file")
@click.argument("infile")
@click.argument("outfile")
def main(include_dir, template_dir, config_file, infile, outfile):
"""Apply templating.
Consume infile, processing it with templating and write the results to outfile.
"""
loaders = []
for d in include_dir:
loaders.append(jinja2.FileSystemLoader(os.path.realpath(d)))
for d in template_dir:
loaders.append(jinja2.FileSystemLoader(os.path.realpath(d)))
# Build a j2 environment using the potentially various loaders..
environment = jinja2.Environment(loader=jinja2.ChoiceLoader(loaders))
# Load a site config
if config_file:
with open(config_file) as f:
site = yaml.safe_load(f.read())
else:
site = {}
# Figure out doing the fontmatter nonsense...
with open(infile, "r") as f:
buff = f.read()
match = re.match(FONTMATTER_PATTERN, buff)
if fontmatter := match.group("fontmatter"):
fontmatter = yaml.safe_load(fontmatter)
else:
fontmatter = {}
# Render the file contents
template = environment.from_string(match.group("content"))
content = template.render(site=site, page=fontmatter)
# If there's a configured `layout:` stick the content in the layout.
if "layout" in fontmatter:
template = environment.get_template(fontmatter.get("layout"))
content = template.render(content=content, site=site, page=fontmatter)
# And dump the results
with open(outfile, "w") as f:
f.write(content)
if __name__ == "__main__":
main()