|
13 | 13 |
|
14 | 14 | import argparse |
15 | 15 | import json |
16 | | -import os |
17 | 16 | import re |
18 | 17 | import shutil |
19 | | -import subprocess |
20 | 18 | import sys |
21 | 19 | import time as _time |
22 | 20 | from pathlib import Path |
@@ -581,6 +579,101 @@ def _build_tutorials_nav(output_dir: Path) -> list: |
581 | 579 | return nav |
582 | 580 |
|
583 | 581 |
|
| 582 | +def _copy_seo_pages(docs_dir: Path, output_dir: Path): |
| 583 | + """Copy pre-generated SEO content from PlatformBackend/docs to Mintlify output. |
| 584 | +
|
| 585 | + Source files: tutorial_{service}_{lang}.md, comparison_{slug}.md, |
| 586 | + use_case_{slug}.md, blog_{slug}.md |
| 587 | + Destination: tutorials/{service}/{lang}.mdx, comparisons/{slug}.mdx, |
| 588 | + use-cases/{slug}.mdx, blog/{slug}.mdx |
| 589 | + """ |
| 590 | + if not docs_dir.is_dir(): |
| 591 | + log(f" WARNING: {docs_dir} not found, skipping SEO pages") |
| 592 | + return |
| 593 | + |
| 594 | + counts: dict[str, int] = {"tutorials": 0, "comparisons": 0, "use-cases": 0, "blog": 0} |
| 595 | + |
| 596 | + for src in sorted(docs_dir.glob("*.md")): |
| 597 | + name = src.stem # e.g. tutorial_claude_python |
| 598 | + |
| 599 | + if name.startswith("tutorial_"): |
| 600 | + # tutorial_{service}_{lang}.md → tutorials/{service}/{lang}.mdx |
| 601 | + rest = name[len("tutorial_"):] # e.g. claude_python or nano-banana_curl |
| 602 | + # Find the last _ that separates service from lang |
| 603 | + for lang in ("python", "javascript", "curl"): |
| 604 | + suffix = f"_{lang}" |
| 605 | + if rest.endswith(suffix): |
| 606 | + service = rest[: -len(suffix)] |
| 607 | + dst = output_dir / "tutorials" / service / f"{lang}.mdx" |
| 608 | + dst.parent.mkdir(parents=True, exist_ok=True) |
| 609 | + _write_seo_mdx(src, dst) |
| 610 | + counts["tutorials"] += 1 |
| 611 | + break |
| 612 | + |
| 613 | + elif name.startswith("comparison_"): |
| 614 | + slug = name[len("comparison_"):] |
| 615 | + dst = output_dir / "comparisons" / f"{slug}.mdx" |
| 616 | + dst.parent.mkdir(parents=True, exist_ok=True) |
| 617 | + _write_seo_mdx(src, dst) |
| 618 | + counts["comparisons"] += 1 |
| 619 | + |
| 620 | + elif name.startswith("use_case_"): |
| 621 | + slug = name[len("use_case_"):] |
| 622 | + dst = output_dir / "use-cases" / f"{slug}.mdx" |
| 623 | + dst.parent.mkdir(parents=True, exist_ok=True) |
| 624 | + _write_seo_mdx(src, dst) |
| 625 | + counts["use-cases"] += 1 |
| 626 | + |
| 627 | + elif name.startswith("blog_"): |
| 628 | + slug = name[len("blog_"):] |
| 629 | + dst = output_dir / "blog" / f"{slug}.mdx" |
| 630 | + dst.parent.mkdir(parents=True, exist_ok=True) |
| 631 | + _write_seo_mdx(src, dst) |
| 632 | + counts["blog"] += 1 |
| 633 | + |
| 634 | + for cat, n in counts.items(): |
| 635 | + log(f" {cat}: {n} files") |
| 636 | + log(f" Total: {sum(counts.values())} SEO pages copied") |
| 637 | + |
| 638 | + |
| 639 | +def _write_seo_mdx(src: Path, dst: Path): |
| 640 | + """Read a Markdown source and write as MDX with frontmatter. |
| 641 | +
|
| 642 | + If the source already has frontmatter (---), keep it. |
| 643 | + Otherwise, extract the first H1 as the title and generate frontmatter. |
| 644 | + """ |
| 645 | + content = src.read_text(encoding="utf-8") |
| 646 | + |
| 647 | + if content.startswith("---"): |
| 648 | + # Already has frontmatter |
| 649 | + dst.write_text(content, encoding="utf-8") |
| 650 | + return |
| 651 | + |
| 652 | + # Extract title from first H1 |
| 653 | + title = "" |
| 654 | + lines = content.split("\n") |
| 655 | + body_start = 0 |
| 656 | + for i, line in enumerate(lines): |
| 657 | + if line.startswith("# "): |
| 658 | + title = line[2:].strip() |
| 659 | + body_start = i + 1 |
| 660 | + break |
| 661 | + |
| 662 | + if not title: |
| 663 | + title = src.stem.replace("_", " ").replace("-", " ").title() |
| 664 | + |
| 665 | + body = "\n".join(lines[body_start:]).strip() |
| 666 | + mdx = f"""--- |
| 667 | +title: "{title}" |
| 668 | +--- |
| 669 | +
|
| 670 | +{body} |
| 671 | +""" |
| 672 | + dst.write_text(mdx, encoding="utf-8") |
| 673 | + |
| 674 | + |
| 675 | + |
| 676 | + |
584 | 677 | def _build_simple_nav(directory: str, output_dir: Path) -> list[str]: |
585 | 678 | """Auto-discover MDX pages in a flat directory.""" |
586 | 679 | d = output_dir / directory |
@@ -1138,21 +1231,13 @@ def main(): |
1138 | 1231 | log("Step 5 done") |
1139 | 1232 |
|
1140 | 1233 | # --------------------------------------------------------------------------- |
1141 | | - # 6. Generate SEO pages (tutorials, comparisons, use-cases, blog) |
| 1234 | + # 6. Copy pre-generated SEO pages (tutorials, comparisons, use-cases, blog) |
1142 | 1235 | # --------------------------------------------------------------------------- |
1143 | 1236 | log() |
1144 | 1237 | log("=" * 60) |
1145 | | - log("Step 6/8: Generate SEO pages (tutorials, comparisons, use-cases, blog)") |
| 1238 | + log("Step 6/8: Copy pre-generated SEO pages from PlatformBackend/docs") |
1146 | 1239 | log("=" * 60) |
1147 | | - seo_script = Path(__file__).parent / "generate_seo_pages.py" |
1148 | | - if seo_script.exists(): |
1149 | | - log("Running SEO page generation...") |
1150 | | - result = subprocess.run( |
1151 | | - [sys.executable, "-u", str(seo_script), "--backend-dir", str(backend_dir), "--output-dir", str(output_dir)], |
1152 | | - env={**os.environ, "PYTHONUNBUFFERED": "1"}, |
1153 | | - ) |
1154 | | - if result.returncode != 0: |
1155 | | - log(f" SEO generation exited with code {result.returncode}") |
| 1240 | + _copy_seo_pages(backend_dir / "docs", output_dir) |
1156 | 1241 | log("Step 6 done") |
1157 | 1242 |
|
1158 | 1243 | # --------------------------------------------------------------------------- |
|
0 commit comments