summaryrefslogtreecommitdiff
path: root/build.py
blob: e253d4c54e8c099dadedbb11c9923d71869094d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env python3

import json
import os
import shutil
from argparse import ArgumentParser
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path

import jinja2
from loguru import logger
from typst import Compiler, TypstError
from watchfiles import watch


@dataclass
class CompiledPage:
    url: str
    title: str
    date: datetime


class Builder:
    def __init__(self, site_dir: Path, static_dir: Path, public_dir: Path) -> None:
        self.site_dir = site_dir
        self.content_dir = site_dir / 'content'
        self.static_dir = static_dir
        self.public_dir = public_dir
        self.compiler = Compiler(root=site_dir)

    def _compile_typst_page(self, path: Path) -> CompiledPage:
        relative_path = path.relative_to(self.content_dir)
        logger.info(f'Typst: {relative_path}')

        output_path = self.public_dir / relative_path.with_suffix('.html')
        output_path.parent.mkdir(parents=True, exist_ok=True)

        self.compiler.compile(
            input=path,
            output=output_path,
            sys_inputs={
                'path': str(relative_path.with_suffix('')),
            },
        )

        metadata = json.loads(
            self.compiler.query(
                selector='<post-meta>',
                field='value',
                one=True,
            )
        )

        return CompiledPage(
            url=str(relative_path.with_suffix('.html')),
            title=metadata['title'],
            date=datetime(
                year=metadata['date_year'],
                month=metadata['date_month'],
                day=metadata['date_day'],
            ),
        )

    def _compile_typst_pages(self) -> list[CompiledPage]:
        return [self._compile_typst_page(path) for path in self.content_dir.rglob('*.typ')]

    def _compile_index_page(self, pages: list[CompiledPage]) -> None:
        pages.sort(key=lambda page: page.date)
        template = jinja2.Template((self.site_dir / 'index.html').read_text())
        (self.public_dir / 'index.html').write_text(template.render(pages=pages))

    def build(self) -> None:
        logger.info(f'Removing {str(self.public_dir)!r}...')
        shutil.rmtree(self.public_dir, ignore_errors=True)
        self.public_dir.mkdir(parents=True, exist_ok=True)

        logger.info('Linking static directory...')
        (self.public_dir / 'static').symlink_to(self.static_dir)

        pages = self._compile_typst_pages()

        logger.info('Building index page...')
        self._compile_index_page(pages)


def main() -> None:
    parser = ArgumentParser()
    parser.add_argument('-w', '--watch', action='store_true', help='watch for changes and rebuild')
    args = parser.parse_args()

    repo_dir = Path(__file__).parent
    site_dir = repo_dir / 'site'
    public_dir = repo_dir / 'public'
    static_dir = repo_dir / 'static'

    os.environ['TYPST_FEATURES'] = 'html'

    builder = Builder(site_dir=site_dir, static_dir=static_dir, public_dir=public_dir)
    builder.build()

    if args.watch:
        logger.info('Watching for changes...')

        for changes in watch(site_dir):
            print(changes)
            try:
                builder.build()
            except TypstError as e:
                logger.exception(e)


if __name__ == '__main__':
    main()