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