From eeecacc3bdc0988115dc7b98f28629b1a6db4465 Mon Sep 17 00:00:00 2001 From: Vinta Chen Date: Sun, 3 May 2026 00:23:14 +0800 Subject: [PATCH] feat(website): generate static pages for subcategories Co-Authored-By: Claude --- website/build.py | 34 ++++++++++++++++++++++++++++++++++ website/tests/test_build.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/website/build.py b/website/build.py index 250c1666..07f26cb9 100644 --- a/website/build.py +++ b/website/build.py @@ -381,6 +381,40 @@ def build(repo_root: Path) -> None: encoding="utf-8", ) + seen_subcats: set[tuple[str, str]] = set() + for category in categories: + for entry in entries: + for sub in entry.get("subcategories", []): + if sub["value"].split(" > ", 1)[0] != category["name"]: + continue + key = (category["slug"], sub["slug"]) + if key in seen_subcats: + continue + seen_subcats.add(key) + sub_entries = [ + e for e in entries + if any(s["value"] == sub["value"] for s in e.get("subcategories", [])) + ] + page_dir = categories_dir / category["slug"] / sub["slug"] + page_dir.mkdir(parents=True, exist_ok=True) + synthetic = { + "name": sub["name"], + "slug": sub["slug"], + "description": "", + "description_html": "", + } + (page_dir / "index.html").write_text( + tpl_category.render( + category=synthetic, + category_url=subcategory_public_url(category["slug"], sub["slug"]), + entries=sub_entries, + total_categories=len(categories), + page_kind="subcategory", + parent_category=category, + ), + encoding="utf-8", + ) + static_src = website / "static" static_dst = site_dir / "static" if static_src.exists(): diff --git a/website/tests/test_build.py b/website/tests/test_build.py index 5fa155fd..d11ba2f3 100644 --- a/website/tests/test_build.py +++ b/website/tests/test_build.py @@ -542,6 +542,41 @@ class TestBuild: assert 'id="hero-category-heading">Browse by category' in html assert 'class="hero-category-link" href="/categories/ai-and-agents/"' in html + def test_build_creates_subcategory_pages(self, tmp_path): + readme = textwrap.dedent("""\ + # T + + --- + + **Web** + + ## Web Frameworks + + - Synchronous + + - [django](https://example.com/django) - Sync framework. + + - Asynchronous + + - [fastapi](https://example.com/fastapi) - Async framework. + + # Contributing + + Done. + """) + self._copy_real_templates(tmp_path) + (tmp_path / "README.md").write_text(readme, encoding="utf-8") + build(tmp_path) + + site = tmp_path / "website" / "output" + sync = (site / "categories" / "web-frameworks" / "synchronous" / "index.html").read_text(encoding="utf-8") + async_ = (site / "categories" / "web-frameworks" / "asynchronous" / "index.html").read_text(encoding="utf-8") + + assert "django" in sync + assert "fastapi" not in sync + assert "fastapi" in async_ + assert "django" not in async_ + def test_build_creates_group_pages(self, tmp_path): readme = textwrap.dedent("""\ # T