Compare commits
No commits in common. "8e64e48097d67c21a4cbeef5afb2e467511cad41" and "ded054147b82eff75f332bc332311c92f063a97e" have entirely different histories.
8e64e48097
...
ded054147b
2
.gitignore
vendored
2
.gitignore
vendored
@ -172,5 +172,3 @@ cython_debug/
|
|||||||
|
|
||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
dist
|
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"cSpell.words": [
|
|
||||||
"stsg"
|
|
||||||
]
|
|
||||||
}
|
|
0
dist/example_page_1/en/index.html
vendored
Normal file
0
dist/example_page_1/en/index.html
vendored
Normal file
0
dist/example_page_2/de/index.html
vendored
Normal file
0
dist/example_page_2/de/index.html
vendored
Normal file
@ -1,30 +0,0 @@
|
|||||||
[project]
|
|
||||||
name = "stsg"
|
|
||||||
dependencies = [
|
|
||||||
"watchdog~=6.0.0"
|
|
||||||
]
|
|
||||||
dynamic = []
|
|
||||||
authors = []
|
|
||||||
readme = "README.md"
|
|
||||||
requires-python = ">=3.10"
|
|
||||||
classifiers = []
|
|
||||||
version = "0.0.0"
|
|
||||||
|
|
||||||
[project.scripts]
|
|
||||||
stsg = "stsg.__main__:build"
|
|
||||||
stsg_dev = "stsg.__main__:hot_reload"
|
|
||||||
|
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["hatchling", "hatch-requirements-txt"]
|
|
||||||
build-backend = "hatchling.build"
|
|
||||||
|
|
||||||
[tool.hatch.build.targets.sdist]
|
|
||||||
include = ["stsg/*.py"]
|
|
||||||
|
|
||||||
[tool.hatch.build.targets.wheel]
|
|
||||||
packages = ["stsg"]
|
|
||||||
|
|
||||||
[tool.hatch.metadata]
|
|
||||||
allow-direct-references = true
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,37 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title>STSG</title>
|
|
||||||
<link rel="stylesheet" href="static/bulma.min.css" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- Header (Navbar) -->
|
|
||||||
<nav
|
|
||||||
class="navbar is-primary"
|
|
||||||
role="navigation"
|
|
||||||
aria-label="main navigation"
|
|
||||||
>
|
|
||||||
<div class="navbar-brand">
|
|
||||||
<a class="navbar-item" href="#">
|
|
||||||
<strong>Static Translated Site Generator</strong>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- Main Content -->
|
|
||||||
<section class="section">
|
|
||||||
<div class="container">
|
|
||||||
<content />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<footer class="footer">
|
|
||||||
<div class="content has-text-centered">
|
|
||||||
<p><strong>STSG</strong> by Hazel. © 2025</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
21551
src/static/bulma.css
vendored
21551
src/static/bulma.css
vendored
File diff suppressed because it is too large
Load Diff
3
src/static/bulma.min.css
vendored
3
src/static/bulma.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1,99 +0,0 @@
|
|||||||
import logging
|
|
||||||
from watchdog.observers import Observer
|
|
||||||
from watchdog.events import FileSystemEventHandler
|
|
||||||
import time
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from .config import SOURCE_DIRECTORY, CODE_DIRECTORY
|
|
||||||
from .build import build as complete_build
|
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
|
||||||
logger = logging.getLogger("stsg")
|
|
||||||
|
|
||||||
|
|
||||||
def build():
|
|
||||||
complete_build()
|
|
||||||
|
|
||||||
|
|
||||||
class MarkdownChangeHandler(FileSystemEventHandler):
|
|
||||||
def on_modified(self, event):
|
|
||||||
if event.is_directory:
|
|
||||||
return
|
|
||||||
|
|
||||||
filename = os.path.basename(event.src_path)
|
|
||||||
if filename.startswith('.') or filename.endswith(('~', '.tmp', '.swp')):
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.info("%s changed, building", event.src_path)
|
|
||||||
build()
|
|
||||||
|
|
||||||
|
|
||||||
class PythonChangeHandler(FileSystemEventHandler):
|
|
||||||
def __init__(self, command):
|
|
||||||
self.logger = logging.getLogger("stsg.hot_reload")
|
|
||||||
|
|
||||||
self.env = os.environ.copy()
|
|
||||||
self.env["IS_CHILD"] = "true"
|
|
||||||
|
|
||||||
self.command = command
|
|
||||||
self.process = self.start_process()
|
|
||||||
|
|
||||||
def start_process(self):
|
|
||||||
self.logger.info("Starting process...")
|
|
||||||
return subprocess.Popen(self.command, env=self.env)
|
|
||||||
|
|
||||||
def restart_process(self):
|
|
||||||
self.logger.info("Restarting process...")
|
|
||||||
self.process.kill()
|
|
||||||
self.process = self.start_process()
|
|
||||||
|
|
||||||
def on_modified(self, event):
|
|
||||||
if event.is_directory:
|
|
||||||
return
|
|
||||||
|
|
||||||
filename = os.path.basename(event.src_path)
|
|
||||||
if filename.startswith('.') or filename.endswith(('~', '.tmp', '.swp')):
|
|
||||||
return
|
|
||||||
|
|
||||||
if event.src_path.endswith(".py"):
|
|
||||||
self.logger.info(f"Detected change: {event.src_path}")
|
|
||||||
self.restart_process()
|
|
||||||
|
|
||||||
def stop_process(self):
|
|
||||||
if self.process:
|
|
||||||
self.process.terminate()
|
|
||||||
|
|
||||||
|
|
||||||
def build_on_change():
|
|
||||||
build()
|
|
||||||
|
|
||||||
observer = Observer()
|
|
||||||
observer.schedule(MarkdownChangeHandler(), path=SOURCE_DIRECTORY, recursive=True)
|
|
||||||
observer.start()
|
|
||||||
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
time.sleep(1) # Keeps the thread alive
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
observer.stop()
|
|
||||||
|
|
||||||
observer.join()
|
|
||||||
|
|
||||||
|
|
||||||
def hot_reload():
|
|
||||||
if os.environ.get("IS_CHILD") == "true":
|
|
||||||
build_on_change()
|
|
||||||
return
|
|
||||||
|
|
||||||
observer = Observer()
|
|
||||||
observer.schedule(PythonChangeHandler(["stsg_dev"]), path=CODE_DIRECTORY, recursive=True)
|
|
||||||
observer.start()
|
|
||||||
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
time.sleep(1) # Keeps the thread alive
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
observer.stop()
|
|
||||||
|
|
||||||
observer.join()
|
|
@ -1,84 +0,0 @@
|
|||||||
import logging
|
|
||||||
import shutil
|
|
||||||
from pathlib import Path
|
|
||||||
import os
|
|
||||||
|
|
||||||
from .config import SOURCE_DIRECTORY, DIST_DIRECTORY, STATIC_DIRECTORY
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("stsg.build")
|
|
||||||
|
|
||||||
|
|
||||||
def copy_static():
|
|
||||||
src = str(Path(SOURCE_DIRECTORY, STATIC_DIRECTORY))
|
|
||||||
dst = str(Path(DIST_DIRECTORY, STATIC_DIRECTORY))
|
|
||||||
|
|
||||||
if not os.path.exists(src):
|
|
||||||
logger.warn("The static folder '%s' wasn't defined.", src)
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.info("copying static files from '%s' to '%r'", src, dst)
|
|
||||||
|
|
||||||
os.makedirs(dst, exist_ok=True)
|
|
||||||
|
|
||||||
for root, dirs, files in os.walk(src):
|
|
||||||
if any(p.startswith(".") for p in Path(root).parts):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Compute relative path from the source root
|
|
||||||
rel_path = os.path.relpath(root, src)
|
|
||||||
dest_dir = os.path.join(dst, rel_path)
|
|
||||||
|
|
||||||
os.makedirs(dest_dir, exist_ok=True)
|
|
||||||
|
|
||||||
for file in files:
|
|
||||||
if file.startswith("."):
|
|
||||||
continue
|
|
||||||
|
|
||||||
src_file = os.path.join(root, file)
|
|
||||||
dest_file = os.path.join(dest_dir, file)
|
|
||||||
shutil.copy2(src_file, dest_file)
|
|
||||||
|
|
||||||
|
|
||||||
class Context:
|
|
||||||
def __init__(self, root: str = SOURCE_DIRECTORY):
|
|
||||||
self.file = Path(root, "index.html")
|
|
||||||
|
|
||||||
current_root = Path(root)
|
|
||||||
while current_root.parts and self.file is None:
|
|
||||||
current_file = Path(current_root, "index.html")
|
|
||||||
if current_file.exists() and current_file.is_file:
|
|
||||||
self.file = current_file
|
|
||||||
|
|
||||||
current_root = current_root.parent
|
|
||||||
|
|
||||||
if self.file is None:
|
|
||||||
logger.error("couldn't find context for %s", root)
|
|
||||||
exit(1)
|
|
||||||
logger.info("%s found context %r", root, str(self.file))
|
|
||||||
|
|
||||||
def get_text(self, placeholder_values: dict):
|
|
||||||
text = self.file.read_text()
|
|
||||||
|
|
||||||
for key, value in placeholder_values.items():
|
|
||||||
text = text.replace(f"<{key}/>", value)
|
|
||||||
text = text.replace(f"<{key} />", value)
|
|
||||||
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def build():
|
|
||||||
logger.info("building static page")
|
|
||||||
copy_static()
|
|
||||||
|
|
||||||
context = Context()
|
|
||||||
|
|
||||||
# debug
|
|
||||||
t = context.get_text({
|
|
||||||
"content": """
|
|
||||||
<h1 class="title">Hello World</h1>
|
|
||||||
<p class="subtitle">My first website with <strong>Bulma</strong>!</p>
|
|
||||||
"""
|
|
||||||
})
|
|
||||||
with Path(DIST_DIRECTORY, "index.html").open("w") as f:
|
|
||||||
f.write(t)
|
|
@ -1,9 +0,0 @@
|
|||||||
SOURCE_DIRECTORY = "src"
|
|
||||||
DIST_DIRECTORY = "dist"
|
|
||||||
|
|
||||||
# relative to SOURCE_DIRECTORY / DIST_DIRECTORY
|
|
||||||
STATIC_DIRECTORY = "static"
|
|
||||||
|
|
||||||
# FOR DEVELOPMENT
|
|
||||||
|
|
||||||
CODE_DIRECTORY = "stsg"
|
|
Loading…
x
Reference in New Issue
Block a user