rift-ssg 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.3
2
+ Name: rift-ssg
3
+ Version: 0.1.0
4
+ Summary: A simple static site generator
5
+ Author: Aleks Rutins
6
+ Author-email: Aleks Rutins <keeper@farthergate.com>
7
+ Requires-Dist: aiohttp>=3.14.5
8
+ Requires-Dist: cyclopts>=5.4.0
9
+ Requires-Dist: frontmatter>=3.0.8
10
+ Requires-Dist: jinja2>=3.1.6
11
+ Requires-Dist: markdown>=3.11.3
12
+ Requires-Dist: watchfiles>=1.3.0
13
+ Requires-Python: >=3.14
14
+ Description-Content-Type: text/markdown
15
+
File without changes
@@ -0,0 +1,28 @@
1
+ [project]
2
+ name = "rift-ssg"
3
+ version = "0.1.0"
4
+ description = "A simple static site generator"
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ dependencies = [
8
+ "aiohttp>=3.14.5",
9
+ "cyclopts>=5.4.0",
10
+ "frontmatter>=3.0.8",
11
+ "jinja2>=3.1.6",
12
+ "markdown>=3.11.3",
13
+ "watchfiles>=1.3.0",
14
+ ]
15
+
16
+ [[project.authors]]
17
+ name = "Aleks Rutins"
18
+ email = "keeper@farthergate.com"
19
+
20
+ [project.scripts]
21
+ rift = "rift_ssg.cli:main"
22
+
23
+ [build-system]
24
+ requires = ["uv_build>=0.13.7,<0.14.0"]
25
+ build-backend = "uv_build"
26
+
27
+ [dependency-groups]
28
+ dev = ["jinja2-time>=0.2.0"]
@@ -0,0 +1,29 @@
1
+ [project]
2
+ name = "rift-ssg"
3
+ version = "0.1.0"
4
+ description = "A simple static site generator"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Aleks Rutins", email = "keeper@farthergate.com" }
8
+ ]
9
+ requires-python = ">=3.14"
10
+ dependencies = [
11
+ "aiohttp>=3.14.5",
12
+ "cyclopts>=5.4.0",
13
+ "frontmatter>=3.0.8",
14
+ "jinja2>=3.1.6",
15
+ "markdown>=3.11.3",
16
+ "watchfiles>=1.3.0",
17
+ ]
18
+
19
+ [project.scripts]
20
+ rift = "rift_ssg.cli:main"
21
+
22
+ [build-system]
23
+ requires = ["uv_build>=0.13.7,<0.14.0"]
24
+ build-backend = "uv_build"
25
+
26
+ [dependency-groups]
27
+ dev = [
28
+ "jinja2-time>=0.2.0",
29
+ ]
@@ -0,0 +1,17 @@
1
+ from pathlib import Path
2
+ from os import write, path
3
+ from rift_ssg.page import Page
4
+
5
+ def build(*pages: Page, out='_build'):
6
+ for p in pages:
7
+ path_to_clean = Path(p.path)
8
+ out_path = Path(out, path_to_clean.relative_to(path_to_clean.anchor))
9
+ if p.path != '/sitemap.xml' and not p.path.endswith('.html'):
10
+ out_path /= 'index.html'
11
+
12
+ out_path.parent.mkdir(parents = True, exist_ok=True)
13
+
14
+ with open(out_path, 'w') as f:
15
+ f.write(p.content(**p.meta))
16
+
17
+
@@ -0,0 +1,27 @@
1
+ import runpy
2
+ import asyncio
3
+ from cyclopts import App
4
+
5
+ from . import server, watcher
6
+
7
+ app = App()
8
+
9
+ @app.command
10
+ def build(script="build.py"):
11
+ """Build the site."""
12
+ runpy.run_path(script)
13
+
14
+ @app.command
15
+ async def dev(script="build.py", dir="_build", port=3000):
16
+ build(script)
17
+
18
+ httpd = await server.run(port, dir)
19
+ watch = asyncio.create_task(watcher.run(script))
20
+
21
+ try:
22
+ await watch
23
+ finally:
24
+ httpd.cleanup()
25
+
26
+ def main():
27
+ asyncio.run(app.run_async())
@@ -0,0 +1,15 @@
1
+ from pathlib import Path
2
+ from aiohttp import web
3
+
4
+ async def run(port=3000, dir="_build"):
5
+ app = web.Application()
6
+ async def index(_, /):
7
+ return web.FileResponse(Path(dir) / 'index.html')
8
+ app.add_routes([
9
+ web.get('/', index),
10
+ web.static('/', dir)
11
+ ])
12
+ runner = web.AppRunner(app)
13
+ await runner.setup()
14
+ await web.TCPSite(runner, "127.0.0.1", port).start()
15
+ return runner
@@ -0,0 +1,9 @@
1
+ import asyncio
2
+ import runpy
3
+ from watchfiles import PythonFilter, awatch
4
+
5
+ async def run(build_script="_build.py"):
6
+ async for _ in awatch('.',
7
+ watch_filter=PythonFilter(extra_extensions=(".md", ".html", ".xl", ".j2"))):
8
+ runpy.run_path(build_script)
9
+ await asyncio.sleep(0)
@@ -0,0 +1,24 @@
1
+ from markdown.extensions.abbr import makeExtension
2
+ from pathlib import Path
3
+ import markupsafe
4
+ import markdown
5
+ from glob import glob
6
+ from frontmatter import Frontmatter
7
+ from rift_ssg.template import TemplateContext
8
+
9
+
10
+ def template_for(path: str, template: TemplateContext, prefix: str):
11
+ data = Frontmatter.read_file(path)
12
+ content = markupsafe.Markup(
13
+ markdown.markdown(data["body"]) if path.endswith(".md") else data["body"]
14
+ )
15
+ return (
16
+ template.set_all(data["attributes"])
17
+ .set("body", content)
18
+ .at(Path(path.removeprefix(prefix)).with_suffix(".html").__str__())
19
+ .set_all(data["attributes"])
20
+ )
21
+
22
+
23
+ def collection(pattern: str, template: TemplateContext, prefix=""):
24
+ return tuple(template_for(path, template, prefix) for path in glob(pattern))
@@ -0,0 +1,21 @@
1
+ from typing import Any
2
+ from collections import defaultdict
3
+ from collections.abc import Callable
4
+ from dataclasses import dataclass, field
5
+
6
+
7
+ @dataclass
8
+ class Page:
9
+ path: str
10
+ content: Callable[..., str]
11
+ meta: defaultdict[str, Any] = field(default_factory=lambda: defaultdict(dict))
12
+
13
+ def set_all(self, params: dict):
14
+ self.meta.update(params)
15
+ return self
16
+
17
+ def set(self, param: str, value: Any):
18
+ self.meta[param] = value
19
+ return self
20
+
21
+
@@ -0,0 +1,10 @@
1
+ from pathlib import Path
2
+ from rift_ssg.page import Page
3
+
4
+ def router(base: str, *pages: Page):
5
+ def page_path(p):
6
+ path = Path(p.path)
7
+ path = path.relative_to(path.anchor)
8
+ return str(base / path)
9
+
10
+ return tuple(Page(page_path(p), p.content) for p in pages)
@@ -0,0 +1,29 @@
1
+ from rift_ssg.page import Page
2
+ from jinja2 import Environment
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass
7
+ class TemplateContext:
8
+ name: str
9
+ env: Environment
10
+ args: tuple
11
+ kwargs: dict
12
+
13
+ def at(self, path: str):
14
+ return Page(path, self)
15
+
16
+ def set(self, key, value):
17
+ self.kwargs[key] = value
18
+ return self
19
+
20
+ def set_all(self, params: dict):
21
+ self.kwargs.update(params)
22
+ return self
23
+
24
+ def __call__(self, *args, **kwargs):
25
+ return self.env.get_template(self.name).render(*self.args, *args, **self.kwargs, **kwargs)
26
+
27
+
28
+ def templates(env: Environment):
29
+ return lambda name, *args, **kwargs: TemplateContext(name, env, args, kwargs)