Conversion of User Guide to the SHH stack (#2781)

This commit is contained in:
Adam Hopkins
2023-09-06 15:44:00 +03:00
committed by GitHub
parent 47215d4635
commit d255d1aae1
332 changed files with 51495 additions and 2013 deletions

View File

View File

@@ -0,0 +1,14 @@
from pathlib import Path
from msgspec import yaml
from webapp.display.layouts.models import GeneralConfig, MenuItem
def load_menu(path: Path) -> list[MenuItem]:
loaded = yaml.decode(path.read_bytes(), type=dict[str, list[MenuItem]])
return loaded["root"]
def load_config(path: Path) -> GeneralConfig:
loaded = yaml.decode(path.read_bytes(), type=GeneralConfig)
return loaded

View File

@@ -0,0 +1,73 @@
from pathlib import Path
from webapp.display.layouts.models import MenuItem
from webapp.display.page import Page, PageRenderer
from webapp.endpoint.view import bp
from webapp.worker.config import load_config, load_menu
from webapp.worker.reload import setup_livereload
from webapp.worker.style import setup_style
from sanic import Request, Sanic, html, redirect
def _compile_sidebar_order(items: list[MenuItem]) -> list[str]:
order = []
for item in items:
if item.path:
order.append(item.path.removesuffix(".html") + ".md")
if item.items:
order.extend(_compile_sidebar_order(item.items))
return order
def create_app(root: Path) -> Sanic:
app = Sanic("Documentation")
app.config.PUBLIC_DIR = root / "public"
app.config.CONTENT_DIR = root / "content"
app.config.CONFIG_DIR = root / "config"
app.config.STYLE_DIR = root / "style"
app.config.NODE_MODULES_DIR = root / "node_modules"
app.config.LANGUAGES = ["en"]
app.config.SIDEBAR = load_menu(
app.config.CONFIG_DIR / "en" / "sidebar.yaml"
)
app.config.NAVBAR = load_menu(app.config.CONFIG_DIR / "en" / "navbar.yaml")
app.config.GENERAL = load_config(
app.config.CONFIG_DIR / "en" / "general.yaml"
)
setup_livereload(app)
setup_style(app)
app.blueprint(bp)
app.static("/assets/", app.config.PUBLIC_DIR / "assets")
@app.before_server_start
async def setup(app: Sanic):
app.ext.dependency(PageRenderer(base_title="TestApp"))
page_order = _compile_sidebar_order(app.config.SIDEBAR)
app.ctx.pages = Page.load_pages(app.config.CONTENT_DIR, page_order)
app.ctx.get_page = Page.get
@app.get("/", name="root")
@app.get("/index.html", name="index")
async def index(request: Request):
return redirect(request.app.url_for("page", language="en", path=""))
@app.get("/<language:str>", name="page-without-path")
@app.get("/<language:str>/<path:path>")
async def page(
request: Request,
page_renderer: PageRenderer,
language: str,
path: str = "",
):
return html(page_renderer.render(request, language, path))
@app.on_request
async def set_language(request: Request):
request.ctx.language = request.match_info.get(
"language", Page.DEFAULT_LANGUAGE
)
return app

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,115 @@
from asyncio import sleep
from multiprocessing import Manager
from pathlib import Path
from queue import Empty, Queue
from typing import Any
import ujson
from sanic import Request, Sanic, Websocket
def setup_livereload(app: Sanic) -> None:
@app.main_process_start
async def main_process_start(app: Sanic):
app.ctx.manager = Manager()
app.shared_ctx.reload_queue = app.ctx.manager.Queue()
@app.main_process_ready
async def main_process_ready(app: Sanic):
app.manager.manage(
"Livereload",
_run_reload_server,
{
"reload_queue": app.shared_ctx.reload_queue,
"debug": app.state.is_debug,
"state": app.manager.worker_state,
},
)
@app.main_process_stop
async def main_process_stop(app: Sanic):
app.ctx.manager.shutdown()
@app.before_server_start
async def before_server_start(app: Sanic):
app.shared_ctx.reload_queue.put("reload")
@app.after_server_start
async def after_server_start(app: Sanic):
app.m.state["ready"] = True
@app.before_server_stop
async def before_server_stop(app: Sanic):
app.m.state["ready"] = False
class Livereload:
SERVER_NAME = "Reloader"
HELLO = {
"command": "hello",
"protocols": [
"http://livereload.com/protocols/official-7",
],
"serverName": SERVER_NAME,
}
def __init__(
self, reload_queue: Queue, debug: bool, state: dict[str, Any]
):
self.reload_queue = reload_queue
self.app = Sanic(self.SERVER_NAME)
self.debug = debug
self.state = state
self.app.static(
"/livereload.js", Path(__file__).parent / "livereload.js"
)
self.app.add_websocket_route(
self.livereload_handler, "/livereload", name="livereload"
)
self.app.add_task(self._listen_to_queue())
self.app.config.EVENT_AUTOREGISTER = True
def run(self):
kwargs = {
"debug": self.debug,
"access_log": False,
"single_process": True,
"port": 35729,
}
self.app.run(**kwargs)
async def _listen_to_queue(self):
while True:
try:
self.reload_queue.get_nowait()
except Empty:
await sleep(0.5)
continue
await self.app.dispatch("livereload.file.reload")
async def livereload_handler(self, request: Request, ws: Websocket):
await ws.recv()
await ws.send(ujson.dumps(self.HELLO))
while True:
await request.app.event("livereload.file.reload")
await self._wait_for_state()
await ws.send(ujson.dumps({"command": "reload", "path": "..."}))
async def _wait_for_state(self):
while True:
states = [
state.get("ready")
for state in self.state.values()
if state.get("server")
]
if all(states):
await sleep(0.5)
break
def _run_reload_server(
reload_queue: Queue, debug: bool, state: dict[str, Any]
):
Livereload(reload_queue, debug, state).run()

View File

@@ -0,0 +1,28 @@
# from scss.compiler import compile_string
from pygments.formatters import html
from sass import compile as compile_scss
from webapp.display.code_style import SanicCodeStyle
from sanic import Sanic
def setup_style(app: Sanic) -> None:
index = app.config.STYLE_DIR / "index.scss"
style_output = app.config.PUBLIC_DIR / "assets" / "style.css"
code_output = app.config.PUBLIC_DIR / "assets" / "code.css"
@app.before_server_start
async def setup(app: Sanic):
scss = compile_scss(
string=index.read_text(),
include_paths=[
str(app.config.NODE_MODULES_DIR),
str(app.config.STYLE_DIR),
],
)
style_output.write_text(scss)
formatter = html.HtmlFormatter(
style=SanicCodeStyle, full=True, cssfile=code_output
)
code_output.write_text(formatter.get_style_defs())