import os
import re
import argparse
import markdown
import datetime
import subprocess
import shutil
from typing import Optional
def kebab_case(s: str) -> str:
return re.sub(r"[ _]", "-", s).lower()
def get_title(filename: str, content: Optional[str] = None) -> str:
if content:
# Check for a top-level header in the content
top_level_header = re.search(r"^#\s(.+)$", content, re.MULTILINE)
if top_level_header:
return top_level_header.group(1).strip()
# Extract the inferred title from the filename
title = filename.replace(".md", "").replace("_", " ")
return title.capitalize()
def parse_wikilinks(content: str) -> str:
# Convert wikilinks with tildes and custom titles
content = re.sub(r'\[\[(~[^|\]]+?)\|([^|\]]+?)\]\]', r'\2', content)
# Convert wikilinks with tildes and without custom titles
content = re.sub(r'\[\[(~[^|\]]+?)\]\]', r'\1', content)
# Convert regular wikilinks with custom titles
content = re.sub(r'\[\[([^~|\]]+?)\|([^~|\]]+?)\]\]', lambda match: f'{match.group(2)}', content)
# Convert regular wikilinks without custom titles
content = re.sub(r'\[\[([^~|\]]+?)\]\]', lambda match: f'{match.group(1)}', content)
return content
def render_template(template: str, title: str, content: str, last_edited: str) -> str:
# Insert title, content, and last_edited into the template
rendered = template.replace("{{ title }}", title)
rendered = rendered.replace("{{ content }}", content)
rendered = rendered.replace("{{ last_edited }}", last_edited)
return rendered
def get_last_edited(path: str) -> str:
try:
# Attempt to get the last Git commit date of the file
last_edited = subprocess.check_output(
["git", "log", "-1", "--format=%cd", "--date=local", path])
return last_edited.decode("utf-8").strip()
except Exception:
# Fallback to the file's modified timestamp
return str(datetime.datetime.fromtimestamp(os.path.getmtime(path)))
def main(input_folder: str, output_folder: str, template_file: str):
# Load the template
with open(template_file, "r") as template_f:
template = template_f.read()
# Go through each markdown file
for root, dirs, files in os.walk(input_folder):
for file in files:
if file.endswith(".md"):
# Process the markdown file
input_file = os.path.join(root, file)
output_subfolder = os.path.join(
output_folder, os.path.relpath(root, input_folder))
output_file = os.path.join(
output_subfolder, f"{kebab_case(file.replace('.md', ''))}.html")
# Read the source file
with open(input_file, "r") as source:
markdown_content = source.read()
html_content = markdown.markdown(
parse_wikilinks(markdown_content), extensions=['codehilite']
)
# Create the output folder if needed
if not os.path.exists(output_subfolder):
os.makedirs(output_subfolder)
# Render the result
title = get_title(file, markdown_content)
last_edited = get_last_edited(input_file)
rendered_content = render_template(template, title, html_content, last_edited)
# Save the rendered HTML file
with open(output_file, "w") as output_f:
output_f.write(rendered_content)
def cmd():
parser = argparse.ArgumentParser(
description="Convert a folder of Markdown files into a simple wiki-style website.")
parser.add_argument('--input', dest='input_folder',
required=True, help='input folder containing Markdown files')
parser.add_argument('--output', dest='output_folder',
required=True, help='output folder for generated HTML files')
parser.add_argument('--template', dest='template_file',
required=True, help='HTML template for the generated files')
args = parser.parse_args()
main(args.input_folder, args.output_folder, args.template_file)
if __name__ == "__main__":
cmd()