diff --git a/website/builder/build.py b/website/builder/build.py index a0a530a5..d0db921c 100644 --- a/website/builder/build.py +++ b/website/builder/build.py @@ -18,7 +18,7 @@ import urllib.parse from collections import Counter from pathlib import Path -from site_assets import copy_referenced_assets, heading_slug, optimize_images, write_animations, write_manifest, write_search_index +from site_assets import VERBATIM, add_image_sizes, copy_referenced_assets, heading_slug, optimize_images, write_animations, write_manifest, write_search_index from check import check BASE = Path(__file__).resolve().parents[1] @@ -37,8 +37,6 @@ SITE_URL = 'https://cyc2018.xyz/' IMAGE = re.compile(r'(]*?\bsrc=["\'])([^"\']+)(["\'])', re.I) MD_IMAGE = re.compile(r'(!\[[^\]]*\]\()((?:[^()\s]|\([^()\n]*\))*)(\))') MD_LINK = re.compile(r'(?`{3,}|~{3,})[^\n]*\n[\s\S]*?^(?P=fence)[`~]*[ \t]*$|', re.M) HTML_LINK = re.compile(r'(]*?\bhref=["\'])([^"\']+)(["\'])', re.I) @@ -51,7 +49,7 @@ def sources(): def image_urls(): found = set() for path in sources(): - text = re.sub(r'```[^\n]*\n[\s\S]*?```|', '', path.read_text()) + text = VERBATIM.sub('', path.read_text()) for pattern in (IMAGE, MD_IMAGE): for match in pattern.finditer(text): url = html.unescape(match[2].strip()) @@ -177,6 +175,44 @@ def render_markdown(path): return ''.join(result + [convert(content[last:])]) +def stub_redirects(dist): + """只剩一个指向其它笔记的链接的旧页面(例如“算法”指向“算法 - 目录”),网站上直接跳转到目标。""" + result = {} + link = re.compile(r'\[[^\]]*\]\((/notes/[^)\s#?]+)\.md(?:#[^)]*)?\)') + for path in sorted((dist / 'notes').glob('*.md')): + text = path.read_text().strip() + targets = link.findall(text) + rest = link.sub('', text) + if len(set(targets)) != 1 or len(rest) > 120 or re.search(r'^#|[\s\S]*?', '', (dist / (parent.lstrip('/') + '.md')).read_text()) + text = VERBATIM.sub('', text) + children = [] + for match in note.findall(text): + child = urllib.parse.unquote(match) + if child not in claimed and (dist / (child.lstrip('/') + '.md')).is_file(): + claimed.add(child) + children.append(child) + if children: + tree[parent] = children + return tree + + def build(optimize=True): global IMAGE_BY_NAME, ANCHORS IMAGE_BY_NAME = repository_images() @@ -206,6 +242,9 @@ def build(optimize=True): topics = [('学习路线', '🚀'), ('算法', '✏️'), ('操作系统', '💻'), ('网络', '☁️'), ('面向对象', '🎨'), ('数据库', '💾'), ('Java', '☕'), ('系统设计', '💡'), ('工具', '🔧'), ('后记', '📝')] + # 卡片按首页中各节的顺序排列,与下面的正文、侧边栏一致;首页中没有的分类不显示 + order = [m[1].strip() for m in re.finditer(r'^## (.+)$', homepage, re.M)] + topics = sorted((t for t in topics if t[0] in order), key=lambda t: order.index(t[0])) cards = '' @@ -228,6 +267,8 @@ def build(optimize=True): elif line.startswith('- ['): sidebar.append(' ' + re.match(r'- \[[^\]]+\]\([^)]+\)', line).group(0)) (dist / '_sidebar.md').write_text('\n'.join(sidebar) + '\n') + tree = section_tree(dist, '\n'.join(sidebar)) + stubs = stub_redirects(dist) (dist / '_404.md').write_text('# 页面暂未找到\n\n请从目录或搜索查找笔记。\n\n[返回首页](/)\n') (dist / '.nojekyll').touch() # 允许收录;学习站每道题的页面列在它的站点地图中(笔记页面在 # 之后,没有单独的地址) @@ -262,6 +303,7 @@ def build(optimize=True): entries.append([question['id'], question['text'], question['heat']]) copy_referenced_assets(dist, ROOT) image_report = optimize_images(dist, CACHE) if optimize else {'converted': 0} + image_report['sized'] = add_image_sizes(dist) index_name, search_report = write_search_index(dist, sorted((dist / 'notes').glob('*.md'))) animations = write_animations(dist) legacy = json.loads((SITE / 'legacy-routes.json').read_text()) @@ -270,6 +312,8 @@ def build(optimize=True): 'window.CS_NOTES_LEGACY_ROUTES = ' + json.dumps(legacy, ensure_ascii=False, separators=(',', ':')) + ';\n' 'window.CS_NOTES_SEARCH_INDEX = "/' + index_name + '";\n' 'window.CS_NOTES_ANIMATIONS = ' + json.dumps(animations, ensure_ascii=False, separators=(',', ':')) + ';\n' + 'window.CS_NOTES_TREE = ' + json.dumps(tree, ensure_ascii=False, separators=(',', ':')) + ';\n' + 'window.CS_NOTES_STUBS = ' + json.dumps(stubs, ensure_ascii=False, separators=(',', ':')) + ';\n' 'window.CS_NOTES_LEARN_TOPICS = ' + json.dumps(learn_topics, ensure_ascii=False, separators=(',', ':')) + ';\n' 'window.CS_NOTES_LEARN_SECTIONS = ' + json.dumps(learn_sections, ensure_ascii=False, separators=(',', ':')) + ';\n') generator = hashlib.sha256()