Skip to content

Instantly share code, notes, and snippets.

@RPGillespie6
Created January 30, 2022 17:59
Show Gist options
  • Save RPGillespie6/b133854b8ebf5a983cf32c26d684cff3 to your computer and use it in GitHub Desktop.
Save RPGillespie6/b133854b8ebf5a983cf32c26d684cff3 to your computer and use it in GitHub Desktop.
Super Simple Static Site Generator (SSSSG)
#!/usr/bin/env python3
"""
Author: Bryan Gillespie
License: MIT
Super Simple Static Site Generator (SSSSG)
A tiny Markdown -> HTML converter
"""
import os
import re
import json
import pathlib
import argparse
# Indentation
II = " "
PAGE_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel ="stylesheet" href="/css/main.css">
<title>Bryan Gillespie</title>
</head>
<body>
<a class="mr-nav" href="/">Home</a>
<a class="mr-nav" href="/about">About</a>
{page_content}
</body>
</html>
""".strip()
# From: https://davidwells.io/snippets/regex-match-markdown-links
# Modified to support markdown of the form: [a](b){c}
REGEX_ANCHOR_STR = '\[([\w\s\d\']+)\]\(((?:\/|https?:\/\/)[\w\d./?=#]+)\)(?:{(.*?)})?'
P_VIDEO = re.compile("!!"+REGEX_ANCHOR_STR)
P_IMG = re.compile("!"+REGEX_ANCHOR_STR)
P_ANCHOR = re.compile(REGEX_ANCHOR_STR)
P_BOLD = re.compile(f"\*\*(.*?)\*\*")
P_ITALICS = re.compile(f"\*(.*?)\*")
def formatLine(line):
line = line.strip()
# Order is important here - video always needs to come before img, etc.
pattern_templates = [
(P_VIDEO, '<video {2}{0}><source src="{1}" type="video/mp4"></video>'),
(P_IMG, '<img {2}src="{1}" alt="{0}">'),
(P_ANCHOR, '<a {2}href="{1}">{0}</a>'),
(P_BOLD, '<strong>{0}</strong>'),
(P_ITALICS, '<em>{0}</em>'),
]
for pattern, template in pattern_templates:
m = True
while m:
m = pattern.search(line)
if m:
cgroups = list(m.groups())
if len(cgroups) == 3:
cgroups[2] = f'class="{cgroups[2]}" ' if cgroups[2] else ""
tag = template.format(*cgroups)
line = re.sub(pattern, tag, line, 1)
return line
def preprocessPage(page):
with open(page) as f:
lines = f.readlines()
metadata = lines[0]
md = lines[1:]
if not metadata.startswith(">> "):
print("Fatal: incorrectly formatted metadata")
os.exit(1)
# Parse metadata as JSON
metadata = json.loads(metadata[3:])
# Make sure md always ends with an empty string line
if md[-1].strip():
md.append("")
return metadata, md
def getHTMLPath(md_path, metadata):
page_name = pathlib.Path(md_path).stem + ".html"
target_path = os.path.abspath(metadata["target_path"].replace("#", "."))
return os.path.join(target_path, page_name)
def processPage(md_path, metadata, md):
html_path = getHTMLPath(md_path, metadata)
page_content = []
current_paragraph = []
for line in md:
line = formatLine(line)
if not line:
if current_paragraph:
paragraph = f"<br>\n{II}".join(current_paragraph)
paragraph = f"<p>{paragraph}</p>"
page_content.append(paragraph)
current_paragraph = []
continue
if line.startswith("#"):
left, heading = line.split(" ", 1)
hll = len(left)
page_content.append(f"<h{hll}>{heading}</h{hll}>")
continue
current_paragraph.append(line)
with open(html_path, "w") as f:
f.write(PAGE_TEMPLATE.format(page_content=f"\n{II}".join(page_content)))
def processPages(pages):
for page in pages:
print(page)
metadata, md = preprocessPage(page)
processPage(page, metadata, md)
def parseArgs():
parser = argparse.ArgumentParser(description="SSSSG - Super Simple Static Site Generator")
parser.add_argument("pages", nargs="+", help="Pages to generate")
# parser.add_argument("-f", "--flag", action="store_true", help="Some bool flag")
return parser.parse_args()
def main():
args = parseArgs()
processPages(args.pages)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment