2021-05-12 18:57:35 +08:00
|
|
|
#!python
|
|
|
|
## Copyright (c) 2021 Thakee Nathees
|
|
|
|
## Licensed under: MIT License
|
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
from markdown import markdown
|
|
|
|
from os.path import join
|
2021-06-04 09:28:42 +08:00
|
|
|
import os, sys, shutil, re
|
2021-05-12 15:57:51 +08:00
|
|
|
|
2021-05-13 03:28:44 +08:00
|
|
|
## TODO: This is a quick and dirty script to generate html
|
|
|
|
## from markdown. Refactor this file in the future.
|
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
## Usage:
|
|
|
|
## to generate pages : python generate.py
|
|
|
|
## to clean pages : python generate.py (-c, --clean)
|
|
|
|
|
|
|
|
TEMPLATE_PATH = 'static/template.html'
|
2021-05-13 03:28:44 +08:00
|
|
|
ROOT_URL = 'https://thakeenathees.github.io/pocketlang/'
|
2021-05-12 15:57:51 +08:00
|
|
|
|
|
|
|
## Home page should be in the SOURCE_DIR.
|
|
|
|
HOME_PAGE = 'home.md'
|
2021-06-03 04:44:01 +08:00
|
|
|
TRY_PAGE = 'try-it-now.html'
|
2021-05-12 15:57:51 +08:00
|
|
|
SOURCE_DIR = 'pages/'
|
|
|
|
TARGET_DIR = 'build/'
|
|
|
|
STATIC_DIR = 'static/'
|
|
|
|
|
2021-05-13 03:28:44 +08:00
|
|
|
## Additional source files of wasm try online page.
|
|
|
|
WASM_SOURCE_FILES = '''\
|
|
|
|
<script type="text/javascript" src="{{ STATIC_DIR }}codejar/codejar.js"></script>
|
|
|
|
<script type="text/javascript" src="{{ STATIC_DIR }}codejar/linenumbers.js"></script>
|
|
|
|
<link rel="stylesheet" type="text/css" href="{{ STATIC_DIR }}codejar/style.css" />
|
|
|
|
|
|
|
|
<script type="text/javascript" src="{{ STATIC_DIR }}prism/prism.js"></script>
|
|
|
|
<link rel="stylesheet" type="text/css" href="{{ STATIC_DIR }}prism/prism.css" />
|
|
|
|
|
2021-05-13 18:31:55 +08:00
|
|
|
<script type="text/javascript" src="{{ STATIC_DIR }}try_now.js"></script>
|
2021-05-13 03:28:44 +08:00
|
|
|
'''
|
|
|
|
|
|
|
|
## Navigation pages in order. Should match the path names.
|
2021-06-03 04:44:01 +08:00
|
|
|
## Any file/folder name shouldn't contain white space.
|
2021-05-13 03:28:44 +08:00
|
|
|
PAGES = [
|
2021-06-03 04:44:01 +08:00
|
|
|
('Getting-Started', [
|
2021-05-13 03:28:44 +08:00
|
|
|
TRY_PAGE,
|
2021-06-04 09:28:42 +08:00
|
|
|
'learn-in-15-minutes.md',
|
2021-06-03 04:44:01 +08:00
|
|
|
'build-from-source.md',
|
2021-05-13 03:28:44 +08:00
|
|
|
'contributing.md',
|
|
|
|
]),
|
|
|
|
|
2021-06-03 04:44:01 +08:00
|
|
|
('Language-API', [
|
2021-05-13 03:28:44 +08:00
|
|
|
'variables.md',
|
|
|
|
'functions.md',
|
2021-06-05 22:47:59 +08:00
|
|
|
'fibers.md',
|
2021-05-19 02:59:09 +08:00
|
|
|
'modules.md',
|
2021-05-13 03:28:44 +08:00
|
|
|
]),
|
|
|
|
]
|
|
|
|
|
|
|
|
def new_context():
|
|
|
|
return {
|
|
|
|
'{{ TITLE }}' : '',
|
2021-06-15 15:37:49 +08:00
|
|
|
'{{ NAVIGATION }}' : '',
|
2021-05-13 03:28:44 +08:00
|
|
|
'{{ CONTENT }}' : '',
|
|
|
|
'{{ HOME_URL }}' : '',
|
|
|
|
'{{ STATIC_DIR }}' : '',
|
|
|
|
}
|
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
def main():
|
|
|
|
|
|
|
|
## Remove generated files and create empty target dir with static files.
|
|
|
|
if os.path.exists(TARGET_DIR):
|
|
|
|
remove_ignore = ( '.git', )
|
|
|
|
for _dir in os.listdir(TARGET_DIR):
|
|
|
|
if _dir in remove_ignore: continue
|
|
|
|
if os.path.isdir(join(TARGET_DIR,_dir)):
|
|
|
|
shutil.rmtree(join(TARGET_DIR, _dir))
|
|
|
|
else:
|
|
|
|
os.remove(join(TARGET_DIR, _dir))
|
2021-05-13 03:28:44 +08:00
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
shutil.copytree(STATIC_DIR, join(TARGET_DIR, STATIC_DIR))
|
|
|
|
open(join(TARGET_DIR, '.nojekyll'), 'w').close()
|
|
|
|
|
|
|
|
## Initialize the template and navigation.
|
|
|
|
template = ''
|
|
|
|
navigation = generate_navigation()
|
|
|
|
with open(TEMPLATE_PATH, 'r') as f:
|
|
|
|
template = f.read()
|
|
|
|
|
|
|
|
## Generate the home page.
|
2021-05-13 03:28:44 +08:00
|
|
|
index_html = join(TARGET_DIR, 'index.html')
|
|
|
|
ctx = generate_page_context(join(SOURCE_DIR, HOME_PAGE), index_html, navigation)
|
|
|
|
write_page(ctx, template, index_html)
|
2021-05-12 15:57:51 +08:00
|
|
|
|
2021-06-03 04:44:01 +08:00
|
|
|
for entry in PAGES: ## entry = ('dirname', [files...])
|
2021-05-13 03:28:44 +08:00
|
|
|
_dir = entry[0]
|
|
|
|
for file in entry[1]:
|
|
|
|
ext = get_validated_ext(file)
|
|
|
|
path = join(SOURCE_DIR, _dir, file)
|
|
|
|
|
2021-06-03 04:44:01 +08:00
|
|
|
dst = ''; path_prefix = _dir.lower().replace(' ', '-') + '-'
|
|
|
|
if ext == '.md':
|
|
|
|
dst = join(TARGET_DIR, path_prefix + file.replace('.md', '.html'))
|
|
|
|
else:
|
|
|
|
dst = join(TARGET_DIR, path_prefix + file)
|
2021-05-13 03:28:44 +08:00
|
|
|
ctx = generate_page_context(path, dst, navigation)
|
|
|
|
|
|
|
|
_template = template
|
|
|
|
if file == TRY_PAGE:
|
|
|
|
_template = template.replace('{{ WASM_SOURCE_FILES }}', WASM_SOURCE_FILES)
|
2021-05-12 15:57:51 +08:00
|
|
|
|
2021-05-13 03:28:44 +08:00
|
|
|
write_page(ctx, _template, dst)
|
2021-05-12 15:57:51 +08:00
|
|
|
pass
|
|
|
|
|
|
|
|
def generate_navigation():
|
|
|
|
navigation = ''
|
2021-05-13 03:28:44 +08:00
|
|
|
for entry in PAGES:
|
|
|
|
_dir = entry[0]
|
|
|
|
navigation += '<div class="navigation">\n'
|
2021-06-03 04:44:01 +08:00
|
|
|
navigation += '<h3><strong>%s</strong></h3>\n' % (_dir.replace('-', ' ').title())
|
2021-05-13 03:28:44 +08:00
|
|
|
navigation += '<ul class="menu">\n'
|
|
|
|
|
|
|
|
for file in entry[1]:
|
|
|
|
ext = get_validated_ext(file)
|
|
|
|
|
|
|
|
link = '' ## Assuming that file name don't contain '.md' at the middle.
|
2021-06-03 04:44:01 +08:00
|
|
|
|
|
|
|
path_prefix = _dir.lower().replace(' ', '-') + '-'
|
|
|
|
if ext == '.md':
|
|
|
|
link = join(ROOT_URL, path_prefix + file.replace('.md', '.html'))
|
|
|
|
else:
|
|
|
|
link = join(ROOT_URL, path_prefix + file)
|
2021-05-13 03:28:44 +08:00
|
|
|
link = link.replace('\\', '/')
|
2021-05-12 15:57:51 +08:00
|
|
|
|
2021-06-03 04:44:01 +08:00
|
|
|
title = file.replace(ext, '').replace('-', ' ').title()
|
2021-05-13 03:28:44 +08:00
|
|
|
navigation += '<li><a href="%s">%s</a></li>\n' % (link, title)
|
2021-05-12 15:57:51 +08:00
|
|
|
|
2021-05-13 03:28:44 +08:00
|
|
|
navigation += '</ul>\n'
|
|
|
|
navigation += '</div>\n'
|
2021-06-03 04:44:01 +08:00
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
return navigation
|
|
|
|
|
|
|
|
|
2021-05-13 03:28:44 +08:00
|
|
|
def generate_page_context(src, dst, navigation):
|
|
|
|
title = path_to_title(src)
|
|
|
|
static_dir = relative_static_dir(dst)
|
|
|
|
content = path_to_content(src)
|
|
|
|
ctx = new_context()
|
2021-06-15 15:37:49 +08:00
|
|
|
ctx[ '{{ TITLE }}' ] = title
|
|
|
|
ctx[ '{{ NAVIGATION }}' ] = navigation
|
|
|
|
ctx[ '{{ CONTENT }}' ] = content
|
|
|
|
ctx[ '{{ HOME_URL }}' ] = ROOT_URL + 'index.html'
|
|
|
|
ctx[ '{{ STATIC_DIR }}' ] = static_dir
|
2021-05-13 03:28:44 +08:00
|
|
|
return ctx;
|
|
|
|
|
|
|
|
def get_validated_ext(path) :
|
|
|
|
ext = ''
|
|
|
|
if path.endswith('.md'): ext = '.md'
|
|
|
|
elif path.endswith('.html'): ext = '.html'
|
|
|
|
else: raise Exception('Expected .md / .html file.')
|
|
|
|
return ext
|
|
|
|
|
|
|
|
## Get the title from the src path.
|
|
|
|
def path_to_title(path):
|
|
|
|
ext = get_validated_ext(path)
|
|
|
|
title = os.path.basename(path).replace(ext, '').title()
|
2021-05-12 15:57:51 +08:00
|
|
|
title += ' - PocketLang'
|
2021-05-13 03:28:44 +08:00
|
|
|
return title
|
2021-05-12 15:57:51 +08:00
|
|
|
|
2021-05-13 03:28:44 +08:00
|
|
|
## Return the static dir relative path.
|
|
|
|
def relative_static_dir(dst):
|
2021-06-03 04:44:01 +08:00
|
|
|
|
|
|
|
return STATIC_DIR ## No more relative paths.
|
|
|
|
|
2021-05-13 03:28:44 +08:00
|
|
|
_dir = os.path.dirname(dst)
|
|
|
|
static_dir = os.path.relpath(join(TARGET_DIR, STATIC_DIR), _dir)
|
2021-05-12 15:57:51 +08:00
|
|
|
static_dir = static_dir.replace('\\', '/')
|
|
|
|
if static_dir[-1] != '/':
|
|
|
|
static_dir += '/'
|
2021-05-13 03:28:44 +08:00
|
|
|
return static_dir
|
2021-05-12 15:57:51 +08:00
|
|
|
|
2021-05-13 03:28:44 +08:00
|
|
|
## Generate html content from the markdown source path.
|
|
|
|
## If the path is an .html file return it's content.
|
|
|
|
def path_to_content(src):
|
|
|
|
|
|
|
|
text = ''
|
2021-06-04 09:28:42 +08:00
|
|
|
with open(src, 'r') as f:
|
|
|
|
text = f.read()
|
2021-05-12 15:57:51 +08:00
|
|
|
|
2021-05-13 03:28:44 +08:00
|
|
|
## If html file we're done.
|
|
|
|
if get_validated_ext(src) == '.html':
|
|
|
|
return text
|
2021-05-12 15:57:51 +08:00
|
|
|
|
2021-06-04 09:28:42 +08:00
|
|
|
assert(src.endswith('.md'))
|
|
|
|
text = custom_md_override(text)
|
2021-05-13 03:28:44 +08:00
|
|
|
content = markdown(text, extensions=['codehilite', 'fenced_code'])
|
2021-05-12 15:57:51 +08:00
|
|
|
|
2021-05-28 03:27:29 +08:00
|
|
|
## A wakey way to inject html overrides to highlight out language
|
|
|
|
## I'm not focusing on generating the pages and this is a wakey way to
|
|
|
|
## do so. This should be done with a good static page generater instead
|
|
|
|
## of this script.
|
|
|
|
return custom_html_override(src, content)
|
|
|
|
|
2021-06-04 09:28:42 +08:00
|
|
|
## Inject our custom markdown text override.
|
|
|
|
def custom_md_override(text):
|
|
|
|
|
|
|
|
## Add html anchor.
|
|
|
|
for pre in ('#', '##', '###'):
|
|
|
|
pattern = '(^' + pre + r' \s*%%(.*)%%\n)'
|
|
|
|
for match, title in re.findall(pattern, text, flags=re.MULTILINE):
|
|
|
|
link = title.strip().lower().replace(' ', '-')
|
|
|
|
text = text.replace(match, f'{pre} {title} <a href="#{link}" name="{link}" class="anchor">#</a>')
|
|
|
|
return text
|
|
|
|
|
2021-05-28 03:27:29 +08:00
|
|
|
## Inject our custom html overrides.
|
|
|
|
def custom_html_override(src, content):
|
2021-05-12 15:57:51 +08:00
|
|
|
## FIXME: I should create a pygment lexer.
|
|
|
|
## A dirty way to inject our keyword (to ruby's).
|
|
|
|
addnl_keywords = [
|
|
|
|
'null', 'from', 'import', 'as', 'func', 'native', 'elif', 'continue'
|
|
|
|
]
|
|
|
|
not_keyword = [
|
|
|
|
'alias', 'begin', 'case', 'class', 'next', 'nil', 'redo', 'rescue',
|
|
|
|
'retry', 'elsif', 'ensure', 'undef', 'unless', 'super', 'until', 'when',
|
|
|
|
'defined',
|
2021-05-13 03:28:44 +08:00
|
|
|
]
|
2021-05-12 15:57:51 +08:00
|
|
|
|
|
|
|
for kw in addnl_keywords:
|
2021-05-13 03:28:44 +08:00
|
|
|
content = content.replace('<span class="n">%s</span>' % kw,
|
|
|
|
'<span class="k">%s</span>' % kw)
|
2021-05-12 15:57:51 +08:00
|
|
|
for nk in not_keyword:
|
2021-05-13 03:28:44 +08:00
|
|
|
content = content.replace('<span class="k">%s</span>' % nk,
|
|
|
|
'<span class="n">%s</span>' % nk)
|
2021-05-28 03:27:29 +08:00
|
|
|
|
|
|
|
## codehilite mark the compilation command as error.
|
2021-06-03 04:44:01 +08:00
|
|
|
content = content.replace('<span class="err">', '<span>')
|
2021-05-28 03:27:29 +08:00
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
return content
|
|
|
|
|
2021-05-13 03:28:44 +08:00
|
|
|
def write_page(ctx, template, dst):
|
|
|
|
_dir = os.path.dirname(dst)
|
|
|
|
if _dir not in ('.', './', '') and not os.path.exists(_dir):
|
|
|
|
os.makedirs(os.path.dirname(dst))
|
|
|
|
|
|
|
|
page = template
|
|
|
|
for key, value in ctx.items():
|
|
|
|
page = page.replace(key, value)
|
|
|
|
|
|
|
|
page = page.replace('{{ WASM_SOURCE_FILES }}', '')
|
|
|
|
|
|
|
|
with open(dst, 'w') as f:
|
|
|
|
f.write(page)
|
|
|
|
|
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
if __name__ == '__main__':
|
2021-05-13 18:31:55 +08:00
|
|
|
_local = False
|
|
|
|
if len(sys.argv) >= 2:
|
|
|
|
if sys.argv[1] == 'local':
|
|
|
|
_local = True
|
2021-06-03 04:44:01 +08:00
|
|
|
#ROOT_URL = 'http://localhost:8000/'
|
|
|
|
|
|
|
|
ROOT_URL = '' ## No more nested directory pages.
|
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
main()
|
2021-05-13 18:31:55 +08:00
|
|
|
|
|
|
|
## Write a batch file to start the server in windows.
|
|
|
|
if _local and os.name == 'nt':
|
|
|
|
with open(join(TARGET_DIR, 'server.bat'), 'w') as f:
|
|
|
|
f.write('python -m http.server 8000')
|
|
|
|
|
|
|
|
print('Static pages generated' +\
|
|
|
|
('for localhost:8000.' if _local else '.'))
|
2021-05-12 15:57:51 +08:00
|
|
|
|