all repos — py-vite @ master

the original vite, written in python

vite/vite.py (view raw)

  1# vite - a simple and minimal static site generator, that JustWorks™
  2# Copyright (c) 2020 Anirudh Oppiliappan <x@icyphox.sh>
  3# Licensed under the MIT license
  4
  5import sys
  6import pathlib
  7import os
  8import jinja2
  9import time
 10import http.server
 11import socketserver
 12import shutil
 13import datetime
 14import re
 15
 16from myrkdown import markdown_path
 17from huepy import *
 18from livereload import Server
 19from subprocess import call
 20
 21
 22# constants
 23PAGES_PATH = "pages/"
 24BUILD_PATH = "build/"
 25TEMPL_PATH = "templates/"
 26TEMPL_FILE = ""
 27PORT = 1911
 28
 29
 30def import_config():
 31    try:
 32        sys.path.append(os.getcwd())
 33        globals()["config"] = __import__("config")
 34        global TEMPL_FILE
 35        TEMPL_FILE = os.path.join(TEMPL_PATH, config.template)
 36    except ImportError:
 37        print(bad("Error: config.py not found."))
 38        print(que("Are you sure you're in a project directory?"))
 39        sys.exit(1)
 40
 41
 42def create_project(path):
 43    try:
 44        abs_path = pathlib.Path(path).resolve()
 45        cur_path = pathlib.Path(".").resolve()
 46        os.makedirs(os.path.join(path, "build"))
 47        os.mkdir(os.path.join(path, "pages"))
 48        os.mkdir(os.path.join(path, "templates"))
 49        os.mkdir(os.path.join(path, "static"))
 50        create_config(path)
 51        create_template(path)
 52        print(good("Created project directory at %s." % (abs_path)))
 53    except FileExistsError:
 54        print(bad("Error: specified path exists."))
 55
 56
 57def create_path(path):
 58    head, tail = os.path.split(path)
 59    now = datetime.datetime.now()
 60    today = now.strftime("%Y-%m-%d")
 61    url = os.path.splitext(os.path.basename(path))[0]
 62
 63    try:
 64        os.makedirs(os.path.join(PAGES_PATH, head))
 65    except FileExistsError:
 66        pass
 67    if os.path.exists(os.path.join(PAGES_PATH, head, tail)):
 68        print(bad("Error: specified path exists."))
 69    else:
 70        with open(os.path.join(PAGES_PATH, head, tail), "w") as f:
 71            to_write = (
 72                """---
 73template:
 74url: {u}
 75title:
 76subtitle:
 77date: {t}
 78---\n"""
 79            ).format(t=today, u=url)
 80            f.write(to_write)
 81        print(good("Created %s.") % (os.path.join(PAGES_PATH, head, tail)))
 82
 83
 84def create_config(path):
 85    with open(os.path.join(path, "config.py"), "w") as f:
 86        f.write(
 87            """# config.py - Vite's configuration script
 88
 89title = ''
 90author = ''
 91header = ''
 92footer = '' 
 93pre_build = []
 94post_build = []
 95template = 'index.html'  # default is index.html\n"""
 96        )
 97
 98
 99def create_template(path):
100    with open(os.path.join(path, "templates", "index.html"), "w") as f:
101        f.write(
102            """<!DOCTYPE html>
103<html>
104<header>
105	{{ header }}
106	<title>
107		{{ title }}	
108	</title>
109</header>
110
111<body>
112	{{ body }}
113</body>
114
115<footer>
116	{{ footer }}
117	<p> {{ author }} </p>
118<footer>
119
120                """
121        )
122
123
124# jinja2
125def jinja_render(html, tmpl):
126    template_loader = jinja2.FileSystemLoader("./")
127    env = jinja2.Environment(loader=template_loader)
128    try:
129        template = env.get_template(tmpl)
130    except jinja2.exceptions.TemplateNotFound:
131        template = env.get_template(TEMPL_FILE)
132    meta = html.metadata
133    output = template.render(
134        title=meta["title"] if "title" in meta else config.title,
135        author=meta["author"] if "author" in meta else config.author,
136        header=meta["header"] if "header" in meta else config.header,
137        url=meta["url"] if "url" in meta else "",
138        footer=meta["footer"] if "footer" in meta else config.footer,
139        date=meta["date"] if "date" in meta else "",
140        subtitle=meta["subtitle"] if "subtitle" in meta else "",
141        body=html,
142    )
143    return output
144
145
146def fm_template(metadata):
147    try:
148        page_template = os.path.join(os.path.join(TEMPL_PATH, metadata["template"]))
149    except KeyError:
150        page_template = TEMPL_FILE
151    return page_template
152
153
154def markdown_render(filename):
155    html = markdown_path(
156        os.path.join(PAGES_PATH, filename),
157        extras=[
158            "metadata",
159            "fenced-code-blocks",
160            "header-ids",
161            "footnotes",
162            "smarty-pants",
163            "tables",
164            "link-patterns",
165        ],
166        link_patterns=[
167            (
168                re.compile(
169                    r"((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+(:[0-9]+)?|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)"
170                ),
171                r"\1",
172            )
173        ],
174    )
175    return html
176
177
178def html_gen():
179    def index_render(f, d=""):
180        index_html = markdown_render(os.path.join(d, f))
181        output = jinja_render(index_html, fm_template(index_html.metadata))
182        with open(os.path.join(BUILD_PATH, d, "index.html"), "w") as ff:
183            ff.write(output)
184            if d:
185                print(run("Rendered " + white("%s/%s") % (d, f)))
186            else:
187                print(run("Rendered " + white("%s") % (f)))
188
189    def normal_render(f, d=""):
190        html_text = markdown_render(os.path.join(d, f))
191        html_file = os.path.splitext(os.path.join(BUILD_PATH, d, f))[0]
192        os.mkdir(html_file)
193        output = jinja_render(html_text, fm_template(html_text.metadata))
194        with open(os.path.join(html_file, "index.html"), "w") as ff:
195            ff.write(output)
196            if d:
197                print(run("Rendered " + white("%s/%s") % (d, f)))
198            else:
199                print(run("Rendered " + white("%s") % (f)))
200
201    for root, dirs, files in os.walk(PAGES_PATH):
202        for d in dirs:
203            os.mkdir(os.path.join(BUILD_PATH, d))
204            for f in os.listdir(os.path.join(PAGES_PATH, d)):
205                if os.path.splitext(f)[1] != ".md":
206                    shutil.copyfile(
207                        os.path.join(PAGES_PATH, d, f), os.path.join(BUILD_PATH, d, f)
208                    )
209                    print(run("Copied " + white("%s/%s") % (d, f)))
210                elif f == "_index.md":
211                    index_render(f, d)
212                else:
213                    normal_render(f, d)
214
215    for f in os.listdir(PAGES_PATH):
216        if os.path.isfile(os.path.join(PAGES_PATH, f)):
217            if os.path.splitext(f)[1] != ".md":
218                shutil.copyfile(
219                    os.path.join(PAGES_PATH, f), os.path.join(BUILD_PATH, f)
220                )
221                print(run("Copied " + white("%s") % (f)))
222            elif f == "_index.md":
223                index_render(f)
224            else:
225                normal_render(f)
226
227
228def server():
229    #    handler = http.server.SimpleHTTPRequestHandler
230    #    os.chdir(os.path.join(os.getcwd(), BUILD_PATH))
231    server = Server()
232    try:
233        print(
234            run(
235                f'Serving the {italic(yellow("build"))} directory at {white(f"http://localhost:{PORT}")}'
236            )
237        )
238        print(white("Ctrl+C") + " to stop.")
239        server.serve(port=PORT, root="build/")
240    except KeyboardInterrupt:
241        print(info("Stopping server."))
242        sys.exit(1)
243
244
245def clean():
246    for root, dirs, files in os.walk(BUILD_PATH):
247        for f in files:
248            os.unlink(os.path.join(root, f))
249        for d in dirs:
250            shutil.rmtree(os.path.join(root, d))
251
252
253def builder():
254    path = os.getcwd()
255    start = time.process_time()
256    if not os.listdir(os.path.join(path, PAGES_PATH)):
257        print(info(italic("pages") + " directory is empty. Nothing to build."))
258        sys.exit(1)
259    else:
260        try:
261            if config.pre_build != "":
262                print(run("Running pre-build actions..."))
263                for s in config.pre_build:
264                    print(info(f"{s}"))
265                    call(s)
266        except AttributeError:
267            pass
268        clean()
269        html_gen()
270        if os.path.exists(os.path.join(os.getcwd(), "static")):
271            shutil.copytree(
272                os.path.join(os.getcwd(), "static"), os.path.join(BUILD_PATH, "static")
273            )
274        try:
275            if config.post_build != "":
276                print(run("Running post-build actions..."))
277                for s in config.post_build:
278                    print(info(f"{s}"))
279                    call([s])
280        except AttributeError:
281            pass
282
283        print(good("Done in %0.5fs." % (time.process_time() - start)))
284