Merge pull request 'feat(docs): Starlight asides and file-tree conversions (site-build-only)' (#78) from starlight-asides-and-filetree into main
Generate documentation / build-docs (push) Successful in 3m7s
Generate documentation / build-docs (push) Successful in 3m7s
Reviewed-on: #78
This commit was merged in pull request #78.
This commit is contained in:
+90
-8
@@ -314,6 +314,26 @@ def _as_ruled_table(para: list[str]) -> str | None:
|
|||||||
return "\n".join(out)
|
return "\n".join(out)
|
||||||
|
|
||||||
|
|
||||||
|
TREE_ROOT_RE = re.compile(r"^[~$][\w./{}-]*/$")
|
||||||
|
TREE_BRANCH_RE = re.compile(r"^[├└]──\s*(\S+)\s*(.*)$")
|
||||||
|
|
||||||
|
|
||||||
|
def _as_file_tree(para: list[str]) -> str | None:
|
||||||
|
"""Render a hand-drawn box-drawing tree as a Starlight <FileTree>, else None."""
|
||||||
|
if len(para) < 2 or not TREE_ROOT_RE.match(para[0].strip()):
|
||||||
|
return None
|
||||||
|
branches = []
|
||||||
|
for line in para[1:]:
|
||||||
|
m = TREE_BRANCH_RE.match(line)
|
||||||
|
if not m:
|
||||||
|
return None
|
||||||
|
branches.append(m.groups())
|
||||||
|
out = ["<FileTree>", f"- {para[0].strip()}"]
|
||||||
|
out += [f" - {name} {desc}".rstrip() for name, desc in branches]
|
||||||
|
out.append("</FileTree>")
|
||||||
|
return "\n".join(out)
|
||||||
|
|
||||||
|
|
||||||
def _render_para(para: list[str], entry_name: str | None, deeper: bool) -> str:
|
def _render_para(para: list[str], entry_name: str | None, deeper: bool) -> str:
|
||||||
"""Render one paragraph of a former indented block.
|
"""Render one paragraph of a former indented block.
|
||||||
|
|
||||||
@@ -335,7 +355,7 @@ def _render_para(para: list[str], entry_name: str | None, deeper: bool) -> str:
|
|||||||
title, body = m.group(1), para[1:]
|
title, body = m.group(1), para[1:]
|
||||||
info = f'fish title="{title}"' if title else "fish"
|
info = f'fish title="{title}"' if title else "fish"
|
||||||
return f"```{info}\n" + "\n".join(body) + "\n```"
|
return f"```{info}\n" + "\n".join(body) + "\n```"
|
||||||
table = _as_ruled_table(para) or _as_table(para)
|
table = _as_ruled_table(para) or _as_table(para) or _as_file_tree(para)
|
||||||
if table is not None:
|
if table is not None:
|
||||||
return table
|
return table
|
||||||
return "\n".join(INDENT + line for line in para)
|
return "\n".join(INDENT + line for line in para)
|
||||||
@@ -385,20 +405,54 @@ def _prettify_block(block: list[str], entry_name: str | None) -> str:
|
|||||||
return "\n\n".join(chunk for chunk in out if chunk.strip())
|
return "\n\n".join(chunk for chunk in out if chunk.strip())
|
||||||
|
|
||||||
|
|
||||||
|
ASIDE_LABELS: dict[str, tuple[str, str, str | None]] = {
|
||||||
|
"NOTE": ("note", "Note", None),
|
||||||
|
"IMPORTANT": ("note", "Important", "star"),
|
||||||
|
"TIP": ("tip", "Tip", None),
|
||||||
|
"HINT": ("tip", "Hint", "question-circle"),
|
||||||
|
"WARNING": ("caution", "Warning", "warning"),
|
||||||
|
"CAUTION": ("caution", "Caution", None),
|
||||||
|
"DANGER": ("danger", "Danger", None),
|
||||||
|
}
|
||||||
|
ASIDE_RE = re.compile(rf"^({'|'.join(ASIDE_LABELS)}):\s*(.*)$")
|
||||||
|
|
||||||
|
|
||||||
|
def _as_aside(para: list[str]) -> str | None:
|
||||||
|
"""Render a `LABEL: ...` flat paragraph as a Starlight <Aside>, else None."""
|
||||||
|
m = ASIDE_RE.match(para[0])
|
||||||
|
if not m:
|
||||||
|
return None
|
||||||
|
label, rest = m.groups()
|
||||||
|
aside_type, title, icon = ASIDE_LABELS[label]
|
||||||
|
body = "\n".join(([rest] if rest else []) + para[1:])
|
||||||
|
attrs = f'type="{aside_type}" title="{title}"'
|
||||||
|
if icon:
|
||||||
|
attrs += f' icon="{icon}"'
|
||||||
|
return f"<Aside {attrs}>\n{body}\n</Aside>"
|
||||||
|
|
||||||
|
|
||||||
def prettify(body: str, entry_name: str | None = None) -> str:
|
def prettify(body: str, entry_name: str | None = None) -> str:
|
||||||
"""Rewrite a body's indented code blocks for the website.
|
"""Rewrite a body's indented code blocks and labeled asides for the website.
|
||||||
|
|
||||||
Site-only: the man page and `config-help` keep reading the untouched
|
Site-only: the man page and `config-help` keep reading the untouched
|
||||||
SSOT, where the indented form is exactly what pandoc wants.
|
SSOT, where the indented form and the `LABEL:` text are exactly what
|
||||||
|
pandoc/`config-help` want.
|
||||||
"""
|
"""
|
||||||
out: list[str] = []
|
out: list[str] = []
|
||||||
block: list[str] = []
|
block: list[str] = []
|
||||||
|
flat: list[str] = []
|
||||||
in_fence = False
|
in_fence = False
|
||||||
|
|
||||||
|
def flush_flat() -> None:
|
||||||
|
out.append(_as_aside(flat) or "\n".join(flat))
|
||||||
|
|
||||||
for line in body.split("\n"):
|
for line in body.split("\n"):
|
||||||
if mt.FENCE_RE.match(line):
|
if mt.FENCE_RE.match(line):
|
||||||
in_fence = not in_fence
|
in_fence = not in_fence
|
||||||
if not in_fence and (line.startswith(INDENT) or (not line.strip() and block)):
|
if not in_fence and (line.startswith(INDENT) or (not line.strip() and block)):
|
||||||
|
if flat:
|
||||||
|
flush_flat()
|
||||||
|
flat.clear()
|
||||||
block.append(line)
|
block.append(line)
|
||||||
continue
|
continue
|
||||||
if block:
|
if block:
|
||||||
@@ -407,8 +461,16 @@ def prettify(body: str, entry_name: str | None = None) -> str:
|
|||||||
out.append(_prettify_block(block, entry_name))
|
out.append(_prettify_block(block, entry_name))
|
||||||
out.append("")
|
out.append("")
|
||||||
block = []
|
block = []
|
||||||
out.append(line)
|
if in_fence or not line.strip():
|
||||||
|
if flat:
|
||||||
|
flush_flat()
|
||||||
|
flat.clear()
|
||||||
|
out.append(line)
|
||||||
|
else:
|
||||||
|
flat.append(line)
|
||||||
|
|
||||||
|
if flat:
|
||||||
|
flush_flat()
|
||||||
if block:
|
if block:
|
||||||
while block and not block[-1].strip():
|
while block and not block[-1].strip():
|
||||||
block.pop()
|
block.pop()
|
||||||
@@ -532,6 +594,24 @@ def _split_entries(body: str) -> tuple[str, list[tuple[str, str]]]:
|
|||||||
return intro, entries
|
return intro, entries
|
||||||
|
|
||||||
|
|
||||||
|
ASTRO_ASIDE_COMPONENTS = {"<Aside": "Aside", "<FileTree": "FileTree"}
|
||||||
|
|
||||||
|
|
||||||
|
def _write_prettified(target: Path, fm: dict, content: str) -> None:
|
||||||
|
"""Write a prettified page, promoting to .mdx when it needs a component import.
|
||||||
|
|
||||||
|
A page stays .md (prettify()'s default, no imports) unless its rendered
|
||||||
|
content actually contains an <Aside> or <FileTree> — the only two
|
||||||
|
components a prettified (non-hand-built) page can contain.
|
||||||
|
"""
|
||||||
|
needed = [name for marker, name in ASTRO_ASIDE_COMPONENTS.items() if marker in content]
|
||||||
|
if needed:
|
||||||
|
imports = f"import {{ {', '.join(needed)} }} from '@astrojs/starlight/components';\n\n"
|
||||||
|
target = target.with_suffix(".mdx")
|
||||||
|
content = imports + content
|
||||||
|
target.write_text(mt.serialize(fm, content))
|
||||||
|
|
||||||
|
|
||||||
def build_site(root: Path, out: Path) -> list[dict]:
|
def build_site(root: Path, out: Path) -> list[dict]:
|
||||||
"""Write the Starlight content tree. Returns the sidebar structure."""
|
"""Write the Starlight content tree. Returns the sidebar structure."""
|
||||||
if out.exists():
|
if out.exists():
|
||||||
@@ -554,7 +634,7 @@ def build_site(root: Path, out: Path) -> list[dict]:
|
|||||||
if not is_function_dir:
|
if not is_function_dir:
|
||||||
target = out / rel
|
target = out / rel
|
||||||
target.parent.mkdir(parents=True, exist_ok=True)
|
target.parent.mkdir(parents=True, exist_ok=True)
|
||||||
target.write_text(mt.serialize(_page_fm(fm), prettify(body)))
|
_write_prettified(target, _page_fm(fm), prettify(body))
|
||||||
if rel.name != "index.md":
|
if rel.name != "index.md":
|
||||||
sidebar.append({"label": fm["title"], "link": "/" + rel.stem + "/"})
|
sidebar.append({"label": fm["title"], "link": "/" + rel.stem + "/"})
|
||||||
continue
|
continue
|
||||||
@@ -571,7 +651,7 @@ def build_site(root: Path, out: Path) -> list[dict]:
|
|||||||
if rel.name == "index.md":
|
if rel.name == "index.md":
|
||||||
target = out / slug_dir / "index.md"
|
target = out / slug_dir / "index.md"
|
||||||
target.parent.mkdir(parents=True, exist_ok=True)
|
target.parent.mkdir(parents=True, exist_ok=True)
|
||||||
target.write_text(mt.serialize(_page_fm(fm), prettify(body)))
|
_write_prettified(target, _page_fm(fm), prettify(body))
|
||||||
# Built explicitly rather than by `autogenerate`, which labels
|
# Built explicitly rather than by `autogenerate`, which labels
|
||||||
# each group with its raw directory slug and republishes this
|
# each group with its raw directory slug and republishes this
|
||||||
# index as a child of the group it already titles.
|
# index as a child of the group it already titles.
|
||||||
@@ -596,8 +676,10 @@ def build_site(root: Path, out: Path) -> list[dict]:
|
|||||||
entry_fm = {"title": title}
|
entry_fm = {"title": title}
|
||||||
if desc:
|
if desc:
|
||||||
entry_fm["description"] = desc
|
entry_fm["description"] = desc
|
||||||
(cat_dir / f"{entry_slug}.md").write_text(
|
_write_prettified(
|
||||||
mt.serialize(entry_fm, prettify(entry_body, title.split()[0]))
|
cat_dir / f"{entry_slug}.md",
|
||||||
|
entry_fm,
|
||||||
|
prettify(entry_body, title.split()[0]),
|
||||||
)
|
)
|
||||||
href = f"/{slug_dir}/{category}/{entry_slug}/"
|
href = f"/{slug_dir}/{category}/{entry_slug}/"
|
||||||
links.append({"label": title, "link": href})
|
links.append({"label": title, "link": href})
|
||||||
|
|||||||
+5
-11
@@ -2603,17 +2603,11 @@ For an interactive alternative to setting these variables by hand, run
|
|||||||
config-settings — a full-screen TUI that flips any category (including C5
|
config-settings — a full-screen TUI that flips any category (including C5
|
||||||
logging) on or off, per session or universally. See its entry in Section 5.
|
logging) on or off, per session or universally. See its entry in Section 5.
|
||||||
|
|
||||||
Notes:
|
NOTE:
|
||||||
|
- Command shadows (rm, cat, ls, ...) react immediately; conf.d-level components (bindings, prompt, abbreviations, hooks) take effect in new shells.
|
||||||
- Command shadows (rm, cat, ls, ...) react immediately; conf.d-level
|
- With aliases disabled, rm falls back to bare `command rm` — files are deleted permanently, not trashed.
|
||||||
components (bindings, prompt, abbreviations, hooks) take effect in
|
- Disabled integration commands (spwin, tab, split, hist, logs, upgrade) print an error naming the variable that disabled them.
|
||||||
new shells.
|
- On CachyOS, the distro fish config's own aliases, history override, and bang-bang bindings are stripped per category as well.
|
||||||
- With aliases disabled, rm falls back to bare `command rm` — files
|
|
||||||
are deleted permanently, not trashed.
|
|
||||||
- Disabled integration commands (spwin, tab, split, hist, logs,
|
|
||||||
upgrade) print an error naming the variable that disabled them.
|
|
||||||
- On CachyOS, the distro fish config's own aliases, history override,
|
|
||||||
and bang-bang bindings are stripped per category as well.
|
|
||||||
|
|
||||||
### Component Reference
|
### Component Reference
|
||||||
|
|
||||||
|
|||||||
@@ -123,17 +123,11 @@ For an interactive alternative to setting these variables by hand, run
|
|||||||
config-settings — a full-screen TUI that flips any category (including C5
|
config-settings — a full-screen TUI that flips any category (including C5
|
||||||
logging) on or off, per session or universally. See its entry in Section 5.
|
logging) on or off, per session or universally. See its entry in Section 5.
|
||||||
|
|
||||||
Notes:
|
NOTE:
|
||||||
|
- Command shadows (rm, cat, ls, ...) react immediately; conf.d-level components (bindings, prompt, abbreviations, hooks) take effect in new shells.
|
||||||
- Command shadows (rm, cat, ls, ...) react immediately; conf.d-level
|
- With aliases disabled, rm falls back to bare `command rm` — files are deleted permanently, not trashed.
|
||||||
components (bindings, prompt, abbreviations, hooks) take effect in
|
- Disabled integration commands (spwin, tab, split, hist, logs, upgrade) print an error naming the variable that disabled them.
|
||||||
new shells.
|
- On CachyOS, the distro fish config's own aliases, history override, and bang-bang bindings are stripped per category as well.
|
||||||
- With aliases disabled, rm falls back to bare `command rm` — files
|
|
||||||
are deleted permanently, not trashed.
|
|
||||||
- Disabled integration commands (spwin, tab, split, hist, logs,
|
|
||||||
upgrade) print an error naming the variable that disabled them.
|
|
||||||
- On CachyOS, the distro fish config's own aliases, history override,
|
|
||||||
and bang-bang bindings are stripped per category as well.
|
|
||||||
|
|
||||||
### Component Reference
|
### Component Reference
|
||||||
|
|
||||||
|
|||||||
+189
-4
@@ -538,16 +538,201 @@ def test_prettify_titles_paths_and_commented_examples():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_as_aside_converts_a_single_line_label():
|
||||||
|
"""A `LABEL: text` line becomes a titled <Aside> with the label's type."""
|
||||||
|
import build_manual
|
||||||
|
|
||||||
|
out = build_manual._as_aside(["TIP: Use `-h` on any function for its help text."])
|
||||||
|
assert out == (
|
||||||
|
'<Aside type="tip" title="Tip">\n'
|
||||||
|
"Use `-h` on any function for its help text.\n"
|
||||||
|
"</Aside>"
|
||||||
|
), f"unexpected aside output:\n{out}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_as_aside_converts_a_label_and_list():
|
||||||
|
"""A label line immediately followed by a list becomes one aside body."""
|
||||||
|
import build_manual
|
||||||
|
|
||||||
|
out = build_manual._as_aside(
|
||||||
|
[
|
||||||
|
"NOTE:",
|
||||||
|
" - Command shadows react immediately.",
|
||||||
|
" - conf.d-level components take effect in new shells.",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
assert out == (
|
||||||
|
'<Aside type="note" title="Note">\n'
|
||||||
|
" - Command shadows react immediately.\n"
|
||||||
|
" - conf.d-level components take effect in new shells.\n"
|
||||||
|
"</Aside>"
|
||||||
|
), f"unexpected aside output:\n{out}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_as_aside_adds_icon_only_where_types_collide():
|
||||||
|
"""WARNING and CAUTION share the `caution` type; only WARNING gets an icon."""
|
||||||
|
import build_manual
|
||||||
|
|
||||||
|
warning = build_manual._as_aside(["WARNING: Deletes files permanently."])
|
||||||
|
caution = build_manual._as_aside(["CAUTION: Slow on large trees."])
|
||||||
|
assert 'icon="warning"' in warning, f"WARNING lost its icon override:\n{warning}"
|
||||||
|
assert "icon=" not in caution, f"CAUTION should use its type's default icon:\n{caution}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_as_aside_rejects_non_label_paragraphs():
|
||||||
|
"""Only the closed set of 7 labels triggers an aside; other WORD: prose does not."""
|
||||||
|
import build_manual
|
||||||
|
|
||||||
|
cases = {
|
||||||
|
"Example prefix": ["Example:", "rm file.txt"],
|
||||||
|
"sentence with a colon": ["Options: pass one of the flags below."],
|
||||||
|
"lowercase label": ["note: this should not become an aside"],
|
||||||
|
}
|
||||||
|
for label, para in cases.items():
|
||||||
|
assert build_manual._as_aside(para) is None, f"{label} was wrongly asided"
|
||||||
|
|
||||||
|
|
||||||
|
def test_prettify_flat_paragraphs_unchanged_when_not_labeled():
|
||||||
|
"""Restructuring prettify()'s loop to buffer flat lines must not alter output for ordinary prose."""
|
||||||
|
import build_manual
|
||||||
|
|
||||||
|
body = "\n".join(
|
||||||
|
[
|
||||||
|
"## Heading",
|
||||||
|
"",
|
||||||
|
"First line of a paragraph",
|
||||||
|
"continued on a second line.",
|
||||||
|
"",
|
||||||
|
"A second paragraph.",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
assert build_manual.prettify(body) == body, "flat prose was altered by the aside buffering"
|
||||||
|
|
||||||
|
|
||||||
|
def test_prettify_converts_a_flat_note_paragraph_to_an_aside():
|
||||||
|
"""A `NOTE:` paragraph surrounded by ordinary prose becomes an <Aside>, prose is untouched."""
|
||||||
|
import build_manual
|
||||||
|
|
||||||
|
body = "\n".join(
|
||||||
|
[
|
||||||
|
"## Heading",
|
||||||
|
"",
|
||||||
|
"Ordinary prose before.",
|
||||||
|
"",
|
||||||
|
"NOTE: Something worth calling out.",
|
||||||
|
"",
|
||||||
|
"Ordinary prose after.",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
out = build_manual.prettify(body)
|
||||||
|
assert "## Heading" in out
|
||||||
|
assert "Ordinary prose before." in out
|
||||||
|
assert "Ordinary prose after." in out
|
||||||
|
assert (
|
||||||
|
'<Aside type="note" title="Note">\nSomething worth calling out.\n</Aside>' in out
|
||||||
|
), f"note paragraph was not converted:\n{out}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_as_file_tree_converts_a_box_drawing_tree():
|
||||||
|
"""A root path plus ├──/└── branches becomes a Starlight <FileTree>."""
|
||||||
|
import build_manual
|
||||||
|
|
||||||
|
para = [
|
||||||
|
"$__fish_user_dots_path/",
|
||||||
|
"├── secrets.fish API keys, tokens, passwords, personal identifiers",
|
||||||
|
"└── local.fish Machine-specific paths, env vars, and sourcing secrets",
|
||||||
|
]
|
||||||
|
out = build_manual._as_file_tree(para)
|
||||||
|
assert out == (
|
||||||
|
"<FileTree>\n"
|
||||||
|
"- $__fish_user_dots_path/\n"
|
||||||
|
" - secrets.fish API keys, tokens, passwords, personal identifiers\n"
|
||||||
|
" - local.fish Machine-specific paths, env vars, and sourcing secrets\n"
|
||||||
|
"</FileTree>"
|
||||||
|
), f"unexpected file tree output:\n{out}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_as_file_tree_rejects_non_trees():
|
||||||
|
"""Returning None is always safe for anything that isn't a root+branches shape."""
|
||||||
|
import build_manual
|
||||||
|
|
||||||
|
cases = {
|
||||||
|
"no root slash": ["$__fish_user_dots_path", "├── secrets.fish"],
|
||||||
|
"single line": ["$__fish_user_dots_path/"],
|
||||||
|
"non-branch second line": ["$__fish_user_dots_path/", "secrets.fish"],
|
||||||
|
}
|
||||||
|
for label, para in cases.items():
|
||||||
|
assert build_manual._as_file_tree(para) is None, f"{label} was wrongly treed"
|
||||||
|
|
||||||
|
|
||||||
|
def test_as_file_tree_rejects_deeper_trees():
|
||||||
|
"""A tree with a second-level (indented) branch line is not detected — falls through to verbatim rendering."""
|
||||||
|
import build_manual
|
||||||
|
|
||||||
|
para = [
|
||||||
|
"~/proj/",
|
||||||
|
"├── src/",
|
||||||
|
"│ └── main.fish",
|
||||||
|
"└── README.md",
|
||||||
|
]
|
||||||
|
assert build_manual._as_file_tree(para) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_customization_notes_render_as_aside():
|
||||||
|
"""The real 07-customization.md NOTE paragraph converts to one intact <Aside>."""
|
||||||
|
import build_manual
|
||||||
|
|
||||||
|
path = Path(__file__).parent / "manual" / "07-customization.md"
|
||||||
|
_, body = mt.parse(path)
|
||||||
|
out = build_manual.prettify(body)
|
||||||
|
assert out.count('<Aside type="note" title="Note">') == 1, f"expected exactly one Note aside:\n{out}"
|
||||||
|
aside = out.split('<Aside type="note" title="Note">')[1].split("</Aside>")[0]
|
||||||
|
assert aside.count(" - ") == 4, f"expected exactly 4 bullets inside the aside:\n{aside}"
|
||||||
|
assert "- Command shadows (rm, cat, ls, ...) react immediately" in aside
|
||||||
|
assert "- With aliases disabled, rm falls back to bare `command rm`" in aside
|
||||||
|
assert "- Disabled integration commands (spwin, tab, split, hist, logs, upgrade)" in aside
|
||||||
|
assert "- On CachyOS, the distro fish config's own aliases" in aside
|
||||||
|
|
||||||
|
|
||||||
|
def test_site_promotes_pages_with_asides_or_filetrees_to_mdx():
|
||||||
|
"""A page whose rendered content contains <Aside> or <FileTree> is written as .mdx."""
|
||||||
|
import build_manual
|
||||||
|
|
||||||
|
docs = Path(__file__).parent
|
||||||
|
with tempfile.TemporaryDirectory() as d:
|
||||||
|
out = Path(d)
|
||||||
|
build_manual.build_site(docs / "manual", out)
|
||||||
|
|
||||||
|
assert (out / "10-personalization.mdx").exists(), "FileTree page was not promoted to .mdx"
|
||||||
|
assert not (out / "10-personalization.md").exists(), "old .md sibling was left behind"
|
||||||
|
|
||||||
|
assert (out / "11-viewing-this-manual.mdx").exists(), "Aside page (NOTE) was not promoted to .mdx"
|
||||||
|
|
||||||
|
assert (out / "07-customization.mdx").exists(), "Aside page (rewritten NOTE) was not promoted to .mdx"
|
||||||
|
|
||||||
|
assert (out / "01-configuration-variables.md").exists(), "plain page was wrongly promoted to .mdx"
|
||||||
|
assert not (out / "01-configuration-variables.mdx").exists(), "plain page should stay .md"
|
||||||
|
|
||||||
|
text = (out / "10-personalization.mdx").read_text()
|
||||||
|
assert "import { FileTree } from '@astrojs/starlight/components';" in text
|
||||||
|
|
||||||
|
text2 = (out / "11-viewing-this-manual.mdx").read_text()
|
||||||
|
assert "import { Aside } from '@astrojs/starlight/components';" in text2
|
||||||
|
|
||||||
|
|
||||||
def test_prettify_is_site_only():
|
def test_prettify_is_site_only():
|
||||||
"""The SSOT keeps the indented form the man-page pipeline depends on."""
|
"""The SSOT keeps the indented form the man-page pipeline depends on."""
|
||||||
import build_manual
|
import build_manual
|
||||||
|
|
||||||
manual = Path(__file__).parent / "manual"
|
manual = Path(__file__).parent / "manual"
|
||||||
|
forbidden = ("```", "<Aside", "<FileTree", ":::")
|
||||||
for path in manual.rglob("*.md"):
|
for path in manual.rglob("*.md"):
|
||||||
assert "```" not in path.read_text(), (
|
text = path.read_text()
|
||||||
f"{path.name} contains a fence: prettify must run at site-build "
|
for marker in forbidden:
|
||||||
"time, never be written back to the SSOT"
|
assert marker not in text, (
|
||||||
)
|
f"{path.name} contains {marker!r}: prettify must run at "
|
||||||
|
"site-build time, never be written back to the SSOT"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_sidebar_has_no_duplicate_functions_entry():
|
def test_sidebar_has_no_duplicate_functions_entry():
|
||||||
|
|||||||
Reference in New Issue
Block a user