feat: docs
This commit is contained in:
3
docs/_ssg/__init__.py
Normal file
3
docs/_ssg/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .compiler import compiler
|
||||
|
||||
__all__ = ["compiler"]
|
||||
132
docs/_ssg/compiler.py
Normal file
132
docs/_ssg/compiler.py
Normal file
@@ -0,0 +1,132 @@
|
||||
import re
|
||||
import typing as t
|
||||
from pathlib import Path
|
||||
|
||||
import mistune
|
||||
from flask import render_template
|
||||
|
||||
from .exceptions import NoPostDefinition
|
||||
from .helpers import get_relative_files_in_the_docs_folder, pytz_dt_now, post_date
|
||||
from .render_engines import HighlightRenderer
|
||||
|
||||
|
||||
def _raw_markdown_processor(raw_markdown: str) -> tuple[t.Optional[list], str, str]:
|
||||
"""
|
||||
:param raw_markdown: The raw markdown to process
|
||||
:return: publish: bool, date: str, title: str, description: str, post: str
|
||||
"""
|
||||
if not raw_markdown.startswith("```"):
|
||||
raise NoPostDefinition
|
||||
|
||||
split_md = raw_markdown.split("```")[1:]
|
||||
raw_meta = split_md[0]
|
||||
|
||||
menu_ptn = re.compile(r"Menu =(.*?)\n", re.IGNORECASE)
|
||||
title_ptn = re.compile(r"Title =(.*?)\n", re.IGNORECASE)
|
||||
|
||||
try:
|
||||
menu = menu_ptn.findall(raw_meta)[0].strip().split("/")
|
||||
except (ValueError, IndexError, TypeError) as _:
|
||||
menu = None
|
||||
|
||||
try:
|
||||
title = title_ptn.findall(raw_meta)[0].strip()
|
||||
except (ValueError, IndexError, TypeError) as _:
|
||||
title = "[Unable to find Title]"
|
||||
|
||||
try:
|
||||
post = "```".join(split_md[1:])
|
||||
except (IndexError, TypeError, ValueError) as _:
|
||||
post = "[Unable to find Post]"
|
||||
|
||||
return menu, title, post
|
||||
|
||||
|
||||
def compiler(docs_dir: Path, markdown_dir: Path):
|
||||
docs_dir.mkdir(exist_ok=True)
|
||||
markdown_dir.mkdir(exist_ok=True)
|
||||
|
||||
markdown_menu = markdown_dir / "__menu__.md"
|
||||
markdown_index = markdown_dir / "__index__.md"
|
||||
|
||||
markdown_menu_dict = dict()
|
||||
|
||||
with open(markdown_menu, mode="r") as menu_file:
|
||||
for line in menu_file.readlines():
|
||||
if line.startswith("-"):
|
||||
line_strip = line.strip()
|
||||
markdown_menu_dict[line_strip.replace("- ", "").strip()] = {
|
||||
"page": "",
|
||||
"pages": [],
|
||||
}
|
||||
continue
|
||||
|
||||
if line.startswith(" ") or line.startswith("\t"):
|
||||
line_strip = line.strip()
|
||||
if line_strip.startswith("-"):
|
||||
markdown_menu_dict[list(markdown_menu_dict.keys())[-1]][
|
||||
"pages"
|
||||
].append({line_strip.replace("- ", "").strip(): ""})
|
||||
|
||||
main_index_html = docs_dir.parent / "index.html"
|
||||
index_html = docs_dir / "index.html"
|
||||
|
||||
docs_dir_files = get_relative_files_in_the_docs_folder(docs_dir)
|
||||
markdown_dir_files = markdown_dir.glob("*.md")
|
||||
html_engine = mistune.create_markdown(renderer=HighlightRenderer())
|
||||
|
||||
html_pages = dict()
|
||||
dt_date = pytz_dt_now()
|
||||
|
||||
main_index_html.unlink(missing_ok=True)
|
||||
main_index_html.write_text(
|
||||
render_template("main_index.html", latest_version=docs_dir.name)
|
||||
)
|
||||
|
||||
for file in docs_dir_files:
|
||||
(docs_dir / f"{file}.html").unlink()
|
||||
|
||||
for file in markdown_dir_files:
|
||||
if "__" in file.stem:
|
||||
continue
|
||||
|
||||
raw_markdown = file.read_text()
|
||||
menu, title, post = _raw_markdown_processor(raw_markdown)
|
||||
html_filename = f'{file.stem.lower().replace(" ", "_")}.html'
|
||||
|
||||
html_pages[html_filename] = {
|
||||
"menu": menu,
|
||||
"title": title,
|
||||
"content": html_engine(post),
|
||||
}
|
||||
|
||||
if menu is not None:
|
||||
if len(menu) == 1:
|
||||
markdown_menu_dict[menu[0]]["page"] = html_filename
|
||||
else:
|
||||
for keys in markdown_menu_dict[menu[0]]["pages"]:
|
||||
if menu[1] in keys.keys():
|
||||
keys[menu[1]] = html_filename
|
||||
|
||||
# write html files
|
||||
for page, meta in html_pages.items():
|
||||
with open(docs_dir / page, mode="w") as html_file:
|
||||
html_file.write(
|
||||
render_template(
|
||||
"__main__.html",
|
||||
menu=markdown_menu_dict,
|
||||
title=meta["title"],
|
||||
date=post_date(dt_date),
|
||||
content=meta["content"],
|
||||
)
|
||||
)
|
||||
|
||||
# write main index.html
|
||||
index_html.write_text(
|
||||
render_template(
|
||||
"index.html",
|
||||
menu=markdown_menu_dict,
|
||||
date=post_date(dt_date),
|
||||
index=html_engine(markdown_index.read_text()),
|
||||
)
|
||||
)
|
||||
38
docs/_ssg/exceptions.py
Normal file
38
docs/_ssg/exceptions.py
Normal file
@@ -0,0 +1,38 @@
|
||||
class NoPostDefinition(Exception):
|
||||
builtin_msg = f"""\n
|
||||
No post definition found!
|
||||
|
||||
{"_" * 10}TOP_OF_FILE{"_" * 10}
|
||||
```
|
||||
Publish = Bool
|
||||
Date = 0000-00-00 00:00:00 +0100 or set-on-compile
|
||||
Title = String
|
||||
Description = String
|
||||
```
|
||||
|
||||
Must be at the top of the file, and must be followed by a blank line.
|
||||
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
return self.builtin_msg
|
||||
|
||||
|
||||
class ErrorInPostDefinition(Exception):
|
||||
builtin_msg = f"""\n
|
||||
There is an error in the post description!
|
||||
|
||||
{"_" * 10}TOP_OF_FILE{"_" * 10}
|
||||
```
|
||||
Publish = Bool
|
||||
Date = 0000-00-00 00:00:00 +0100 or set-on-compile
|
||||
Title = String
|
||||
Description = String
|
||||
```
|
||||
|
||||
Must be at the top of the file, and must be followed by a blank line.
|
||||
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
return self.builtin_msg
|
||||
53
docs/_ssg/helpers.py
Normal file
53
docs/_ssg/helpers.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import re
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from pytz import timezone
|
||||
|
||||
local_tz = timezone("Europe/London")
|
||||
|
||||
|
||||
def pytz_dt_now() -> datetime:
|
||||
return datetime.now(local_tz)
|
||||
|
||||
|
||||
def pytz_dt_epoch() -> float:
|
||||
return pytz_dt_now().timestamp()
|
||||
|
||||
|
||||
def pytz_dt_now_str(mask: str = "%Y-%m-%d %H:%M:%S %z") -> str:
|
||||
return datetime.now(local_tz).strftime(mask)
|
||||
|
||||
|
||||
def pytz_dt_to_str(pytz_dt: datetime, mask: str = "%Y-%m-%d %H:%M:%S %z") -> str:
|
||||
return pytz_dt.strftime(mask)
|
||||
|
||||
|
||||
def pytz_dt_str_to_dt(pytz_dt_str: str) -> datetime:
|
||||
"""
|
||||
:param pytz_dt_str: "2020-01-01 00:00:00 +0000"
|
||||
"""
|
||||
return datetime.strptime(pytz_dt_str, "%Y-%m-%d %H:%M:%S %z")
|
||||
|
||||
|
||||
def post_date(pytz_dt: datetime) -> str:
|
||||
return pytz_dt.strftime("%a, %d %b %Y")
|
||||
|
||||
|
||||
def switch_date(content, new_date):
|
||||
pattern = re.compile(r'date="(.*?)"', re.IGNORECASE)
|
||||
return pattern.sub(f'date="{new_date}"', content)
|
||||
|
||||
|
||||
def get_relative_files_in_the_docs_folder(docs_dir: Path) -> list:
|
||||
_ = []
|
||||
for f in docs_dir.glob("*.html"):
|
||||
if f.stem == "index":
|
||||
continue
|
||||
_.append(f.stem)
|
||||
|
||||
return _
|
||||
|
||||
|
||||
def excessive_br_cleanup(base_xml: str) -> str:
|
||||
return base_xml.replace("</p><br/>", "</p>").replace("<ol><br/>", "<ol>")
|
||||
18
docs/_ssg/render_engines.py
Normal file
18
docs/_ssg/render_engines.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import mistune
|
||||
from pygments import highlight
|
||||
from pygments.formatters import HtmlFormatter
|
||||
from pygments.lexers import get_lexer_by_name
|
||||
from pygments.util import ClassNotFound
|
||||
|
||||
|
||||
class HighlightRenderer(mistune.HTMLRenderer):
|
||||
def block_code(self, code, info=None):
|
||||
if info:
|
||||
if info == "jinja2":
|
||||
info = "jinja"
|
||||
try:
|
||||
lexer = get_lexer_by_name(info, stripall=True)
|
||||
except ClassNotFound:
|
||||
lexer = get_lexer_by_name("text", stripall=True)
|
||||
return highlight(code, lexer, HtmlFormatter())
|
||||
return "<pre><code>" + mistune.escape(code) + "</code></pre>"
|
||||
Reference in New Issue
Block a user