Skip to content

Commit 3ee0e31

Browse files
committed
feat: Webサイトの生成スクリプトのYAMLの入出力処理を削除
1 parent f739ea4 commit 3ee0e31

File tree

1 file changed

+163
-129
lines changed

1 file changed

+163
-129
lines changed

gen.py

+163-129
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,166 @@
1-
import jinja2
1+
"""typst-docsが出力したJSONファイルを読み込んでHTMLファイルを出力する"""
2+
23
import json
3-
import os
44
import shutil
5-
import yaml
6-
7-
8-
def str_presenter(dumper, data):
9-
if len(data.splitlines()) > 1: # check for multiline string
10-
return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
11-
return dumper.represent_scalar('tag:yaml.org,2002:str', data)
12-
13-
14-
yaml.add_representer(str, str_presenter)
15-
16-
17-
def translate_with_yaml(page):
18-
if page['body']['kind'] in ['func', 'type', 'group', 'category']:
19-
route = page['route'][:-1] if page['route'].endswith('/') else page['route']
20-
assert route.startswith('/docs/reference/')
21-
if page['body']['kind'] == 'category':
22-
path = ['category'] + route[len('/docs/reference/'):].split('/')
23-
else:
24-
path = route[len('/docs/reference/'):].split('/')
25-
assert len(path) == 2, str(path) + ' ' + route
26-
# if docs/i18n/{path[0]} not exists, create it
27-
if not os.path.exists('docs/i18n/' + path[0]):
28-
os.mkdir('docs/i18n/' + path[0])
29-
# without quotes and with indent
30-
en_path = 'docs/i18n/' + path[0] + '/' + path[1] + '-en.yaml'
31-
ja_path = 'docs/i18n/' + path[0] + '/' + path[1] + '-ja.yaml'
32-
with open(en_path, 'w', encoding='utf-8') as f:
33-
yaml.dump(page, f, allow_unicode=True, default_flow_style=False,
34-
indent=2, sort_keys=False, encoding='utf-8')
35-
if not os.path.exists(ja_path):
36-
with open(ja_path, 'w', encoding='utf-8') as f:
37-
yaml.dump(page, f, allow_unicode=True, default_flow_style=False,
38-
indent=2, sort_keys=False, encoding='utf-8')
39-
if os.path.exists(ja_path):
40-
with open(ja_path, 'r', encoding='utf-8') as f:
41-
page = yaml.load(f, Loader=yaml.FullLoader)
42-
for i in range(len(page['children'])):
43-
page['children'][i] = translate_with_yaml(page['children'][i])
44-
return page
45-
46-
47-
type2class_map = {
48-
'none': 'pill-kw',
49-
'auto': 'pill-kw',
50-
'function': 'pill-fn',
51-
'string': 'pill-str',
52-
'str': 'pill-str',
53-
'content': 'pill-con',
54-
'color': 'pill-col',
55-
'bool': 'pill-bool',
56-
'boolean': 'pill-bool',
57-
'integer': 'pill-num',
58-
'int': 'pill-num',
59-
'ratio': 'pill-num',
60-
'length': 'pill-num',
61-
'relative length': 'pill-num',
62-
'float': 'pill-num',
63-
'angle': 'pill-num',
64-
'fraction': 'pill-num',
65-
}
66-
67-
68-
def type2class(type):
69-
return type2class_map.get(type, 'pill-obj')
70-
71-
def gen_path(item):
72-
return ''.join([s + '.' for s in item['path']])
73-
74-
75-
def render_jinja_html(template_loc, file_name, **context):
76-
return jinja2.Environment(
77-
loader=jinja2.FileSystemLoader(template_loc + '/')
78-
).get_template(file_name).render(context)
79-
80-
81-
if __name__ == '__main__':
82-
83-
flattern_pages = []
84-
index = 0
85-
86-
def dfs(page, docs):
87-
flattern_pages.append(page)
88-
for child in page['children']:
89-
dfs(child, docs)
90-
91-
def render_to_files(page, docs, path):
92-
global index
93-
prev = flattern_pages[index - 1] if index > 0 else None
94-
next = flattern_pages[index +
95-
1] if index < len(flattern_pages) - 1 else None
96-
if not os.path.exists('./dist' + page['route']):
97-
os.makedirs('./dist' + page['route'])
98-
with open('./dist' + page['route'] + ('/' if not page['route'].endswith('/') else '') + 'index.html', 'w', encoding='utf-8') as f:
99-
f.write(render_jinja_html('./templates/', page['body']['kind'] + '_template.html.j2',
100-
docs=docs, path=path, prev=prev, next=next, type2class=type2class, gen_path=gen_path, **page))
101-
index += 1
102-
for child in page['children']:
103-
render_to_files(child, docs, path + [child])
104-
105-
# cargo test --package typst-docs --lib -- tests::test_docs --exact --nocapture
106-
107-
# clean dist
108-
if os.path.exists('./dist'):
109-
shutil.rmtree('./dist')
110-
111-
# copy static to dist
112-
shutil.copytree('./static', './dist')
113-
114-
# delete ./dist/assets/docs
115-
if os.path.exists('./dist/assets/docs'):
116-
shutil.rmtree('./dist/assets/docs')
117-
118-
# copy assets/docs to dist/assets/docs
119-
shutil.copytree('./assets/docs', './dist/assets/docs')
120-
121-
# load docs.json and render to files
122-
with open('./assets/docs.json', 'r', encoding='utf-8') as f:
5+
from pathlib import Path
6+
from typing import Any, TypedDict
7+
8+
import jinja2
9+
10+
11+
class OutlineItemDict(TypedDict):
12+
"""アウトライン情報"""
13+
14+
id: str
15+
name: str
16+
children: list["OutlineItemDict"]
17+
18+
19+
class BodyDict(TypedDict):
20+
"""本文情報"""
21+
22+
kind: str
23+
# NOTE: dict[str, Any]の正確な型はtypst-docsの出力を参照してください
24+
content: str | dict[str, Any]
25+
26+
27+
class PageDict(TypedDict):
28+
"""ページ情報"""
29+
30+
route: str
31+
title: str
32+
description: str
33+
part: str | None
34+
outline: list[OutlineItemDict]
35+
body: BodyDict
36+
children: list["PageDict"]
37+
38+
39+
def type2class(parameter_type: str) -> str:
40+
"""関数の引数の型名からCSSのクラス名を取得する"""
41+
type2class_map: dict[str, str] = {
42+
"none": "pill-kw",
43+
"auto": "pill-kw",
44+
"function": "pill-fn",
45+
"string": "pill-str",
46+
"str": "pill-str",
47+
"content": "pill-con",
48+
"color": "pill-col",
49+
"bool": "pill-bool",
50+
"boolean": "pill-bool",
51+
"integer": "pill-num",
52+
"int": "pill-num",
53+
"ratio": "pill-num",
54+
"length": "pill-num",
55+
"relative length": "pill-num",
56+
"float": "pill-num",
57+
"angle": "pill-num",
58+
"fraction": "pill-num",
59+
}
60+
return type2class_map.get(parameter_type, "pill-obj")
61+
62+
63+
def gen_path(item: dict[str, Any]) -> str:
64+
"""pathを連結する"""
65+
return "".join([s + "." for s in item["path"]])
66+
67+
68+
def render_jinja_html(
69+
template_loc: str,
70+
file_name: str,
71+
**context: Any, # noqa: ANN401
72+
) -> str:
73+
"""Jinja2テンプレートをレンダリングする"""
74+
return (
75+
jinja2.Environment(
76+
loader=jinja2.FileSystemLoader(f"{template_loc}/"),
77+
autoescape=False, # noqa: S701
78+
)
79+
.get_template(file_name)
80+
.render(context)
81+
)
82+
83+
84+
if __name__ == "__main__":
85+
# 出力先のディレクトリを初期化する
86+
if Path("./dist").exists():
87+
shutil.rmtree("./dist")
88+
89+
shutil.copytree("./static", "./dist")
90+
91+
if Path("./dist/assets/docs").exists():
92+
shutil.rmtree("./dist/assets/docs")
93+
94+
shutil.copytree("./assets/docs", "./dist/assets/docs")
95+
96+
# typst-docsが出力したJSONファイルを読み込む
97+
docs: list[PageDict] = []
98+
with Path("./assets/docs.json").open(encoding="utf-8") as f:
12399
docs = json.load(f)
124-
# if docs/i18n not exists, create it
125-
if not os.path.exists('docs/i18n'):
126-
os.mkdir('docs/i18n')
127-
for i in range(len(docs)):
128-
docs[i] = translate_with_yaml(docs[i])
129-
for page in docs:
130-
dfs(page, docs)
131-
for page in docs:
132-
render_to_files(page, docs, [page])
100+
101+
# ドキュメントの階層構造を平坦化する
102+
# パンくずリストと前後のページ情報を取得するため
103+
flattened_pages: list[PageDict] = [] # 平坦化されたページ情報のリスト
104+
page_paths: list[list[PageDict]] = [] # ページ情報[i]のパス情報
105+
106+
def _flatten_docs(page: PageDict, page_path: list[PageDict]) -> None:
107+
"""ドキュメントの階層構造を平坦化する
108+
109+
Args:
110+
page (PageDict): ページ情報
111+
page_path (list[PageDict]): ページ情報のパス
112+
"""
113+
flattened_pages.append(page)
114+
page_paths.append(page_path)
115+
for child_page in page["children"]:
116+
_flatten_docs(child_page, [*page_path, child_page])
117+
118+
for page in docs:
119+
_flatten_docs(page, [page])
120+
121+
# ドキュメントをHTMLファイルに出力する
122+
def _render_to_file(
123+
page: PageDict,
124+
path: list[PageDict],
125+
page_index: int,
126+
) -> None:
127+
"""ページをHTMLファイルに出力する
128+
129+
Args:
130+
page (PageDict): ページ情報
131+
path (list[Any]): パンくずリストを生成するためのページ情報のリスト
132+
page_index (int): 前後のページ情報を取得するためのページのインデックス
133+
"""
134+
previous_page: PageDict | None = (
135+
flattened_pages[page_index - 1] if page_index > 0 else None
136+
)
137+
next_page: PageDict | None = (
138+
flattened_pages[page_index + 1]
139+
if page_index < len(flattened_pages) - 1
140+
else None
141+
)
142+
if not Path(f"./dist{page['route']}").exists():
143+
Path(f"./dist{page['route']}").mkdir(parents=True)
144+
with Path(
145+
f"./dist{page['route']}{'/' if not page['route'].endswith('/') else ''}index.html", # noqa: E501
146+
).open(
147+
mode="w",
148+
encoding="utf-8",
149+
) as f:
150+
f.write(
151+
render_jinja_html(
152+
"./templates/",
153+
f"{page['body']['kind']}_template.html.j2",
154+
docs=docs,
155+
path=path,
156+
prev=previous_page,
157+
next=next_page,
158+
type2class=type2class,
159+
gen_path=gen_path,
160+
**page,
161+
),
162+
)
163+
print(f"Generated: {page['route']}") # noqa: T201
164+
165+
for i, (page, path) in enumerate(zip(flattened_pages, page_paths, strict=True)):
166+
_render_to_file(page, path, i)

0 commit comments

Comments
 (0)