import sys
import pathlib
import os
import jinja2
import time
import http.server
import socketserver
import shutil
import datetime
import re
from myrkdown import markdown_path
from huepy import *
from livereload import Server
from subprocess import call
PAGES_PATH = "pages/"
BUILD_PATH = "build/"
TEMPL_PATH = "templates/"
TEMPL_FILE = ""
PORT = 1911
def import_config():
try:
sys.path.append(os.getcwd())
globals()["config"] = __import__("config")
global TEMPL_FILE
TEMPL_FILE = os.path.join(TEMPL_PATH, config.template)
except ImportError:
print(bad("Error: config.py not found."))
print(que("Are you sure you're in a project directory?"))
sys.exit(1)
def create_project(path):
try:
abs_path = pathlib.Path(path).resolve()
cur_path = pathlib.Path(".").resolve()
os.makedirs(os.path.join(path, "build"))
os.mkdir(os.path.join(path, "pages"))
os.mkdir(os.path.join(path, "templates"))
os.mkdir(os.path.join(path, "static"))
create_config(path)
create_template(path)
print(good("Created project directory at %s." % (abs_path)))
except FileExistsError:
print(bad("Error: specified path exists."))
def create_path(path):
head, tail = os.path.split(path)
now = datetime.datetime.now()
today = now.strftime("%Y-%m-%d")
url = os.path.splitext(os.path.basename(path))[0]
try:
os.makedirs(os.path.join(PAGES_PATH, head))
except FileExistsError:
pass
if os.path.exists(os.path.join(PAGES_PATH, head, tail)):
print(bad("Error: specified path exists."))
else:
with open(os.path.join(PAGES_PATH, head, tail), "w") as f:
to_write = (
"""---
template:
url: {u}
title:
subtitle:
date: {t}
---\n"""
).format(t=today, u=url)
f.write(to_write)
print(good("Created %s.") % (os.path.join(PAGES_PATH, head, tail)))
def create_config(path):
with open(os.path.join(path, "config.py"), "w") as f:
f.write(
"""# config.py - Vite's configuration script
title = ''
author = ''
header = ''
footer = ''
pre_build = []
post_build = []
template = 'index.html' # default is index.html\n"""
)
def create_template(path):
with open(os.path.join(path, "templates", "index.html"), "w") as f:
f.write(
"""<!DOCTYPE html>
<html>
<header>
{{ header }}
<title>
{{ title }}
</title>
</header>
<body>
{{ body }}
</body>
<footer>
{{ footer }}
<p> {{ author }} </p>
<footer>
"""
)
def jinja_render(html, tmpl):
template_loader = jinja2.FileSystemLoader("./")
env = jinja2.Environment(loader=template_loader)
try:
template = env.get_template(tmpl)
except jinja2.exceptions.TemplateNotFound:
template = env.get_template(TEMPL_FILE)
meta = html.metadata
output = template.render(
title=meta["title"] if "title" in meta else config.title,
author=meta["author"] if "author" in meta else config.author,
header=meta["header"] if "header" in meta else config.header,
url=meta["url"] if "url" in meta else "",
footer=meta["footer"] if "footer" in meta else config.footer,
date=meta["date"] if "date" in meta else "",
subtitle=meta["subtitle"] if "subtitle" in meta else "",
body=html,
)
return output
def fm_template(metadata):
try:
page_template = os.path.join(os.path.join(TEMPL_PATH, metadata["template"]))
except KeyError:
page_template = TEMPL_FILE
return page_template
def markdown_render(filename):
html = markdown_path(
os.path.join(PAGES_PATH, filename),
extras=[
"metadata",
"fenced-code-blocks",
"header-ids",
"footnotes",
"smarty-pants",
"tables",
"link-patterns",
],
link_patterns=[
(
re.compile(
r"((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+(:[0-9]+)?|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)"
),
r"\1",
)
],
)
return html
def html_gen():
def index_render(f, d=""):
index_html = markdown_render(os.path.join(d, f))
output = jinja_render(index_html, fm_template(index_html.metadata))
with open(os.path.join(BUILD_PATH, d, "index.html"), "w") as ff:
ff.write(output)
if d:
print(run("Rendered " + white("%s/%s") % (d, f)))
else:
print(run("Rendered " + white("%s") % (f)))
def normal_render(f, d=""):
html_text = markdown_render(os.path.join(d, f))
html_file = os.path.splitext(os.path.join(BUILD_PATH, d, f))[0]
os.mkdir(html_file)
output = jinja_render(html_text, fm_template(html_text.metadata))
with open(os.path.join(html_file, "index.html"), "w") as ff:
ff.write(output)
if d:
print(run("Rendered " + white("%s/%s") % (d, f)))
else:
print(run("Rendered " + white("%s") % (f)))
for root, dirs, files in os.walk(PAGES_PATH):
for d in dirs:
os.mkdir(os.path.join(BUILD_PATH, d))
for f in os.listdir(os.path.join(PAGES_PATH, d)):
if os.path.splitext(f)[1] != ".md":
shutil.copyfile(
os.path.join(PAGES_PATH, d, f), os.path.join(BUILD_PATH, d, f)
)
print(run("Copied " + white("%s/%s") % (d, f)))
elif f == "_index.md":
index_render(f, d)
else:
normal_render(f, d)
for f in os.listdir(PAGES_PATH):
if os.path.isfile(os.path.join(PAGES_PATH, f)):
if os.path.splitext(f)[1] != ".md":
shutil.copyfile(
os.path.join(PAGES_PATH, f), os.path.join(BUILD_PATH, f)
)
print(run("Copied " + white("%s") % (f)))
elif f == "_index.md":
index_render(f)
else:
normal_render(f)
def server():
server = Server()
try:
print(
run(
f'Serving the {italic(yellow("build"))} directory at {white(f"http://localhost:{PORT}")}'
)
)
print(white("Ctrl+C") + " to stop.")
server.serve(port=PORT, root="build/")
except KeyboardInterrupt:
print(info("Stopping server."))
sys.exit(1)
def clean():
for root, dirs, files in os.walk(BUILD_PATH):
for f in files:
os.unlink(os.path.join(root, f))
for d in dirs:
shutil.rmtree(os.path.join(root, d))
def builder():
path = os.getcwd()
start = time.process_time()
if not os.listdir(os.path.join(path, PAGES_PATH)):
print(info(italic("pages") + " directory is empty. Nothing to build."))
sys.exit(1)
else:
try:
if config.pre_build != "":
print(run("Running pre-build actions..."))
for s in config.pre_build:
print(info(f"{s}"))
call(s)
except AttributeError:
pass
clean()
html_gen()
if os.path.exists(os.path.join(os.getcwd(), "static")):
shutil.copytree(
os.path.join(os.getcwd(), "static"), os.path.join(BUILD_PATH, "static")
)
try:
if config.post_build != "":
print(run("Running post-build actions..."))
for s in config.post_build:
print(info(f"{s}"))
call([s])
except AttributeError:
pass
print(good("Done in %0.5fs." % (time.process_time() - start)))