added pandoc as renderer

This commit is contained in:
2026-02-08 18:03:05 -05:00
parent a56cd665b0
commit 164cb5d980
6 changed files with 33 additions and 9 deletions

View File

@@ -141,6 +141,7 @@ Each managed directory must contain a `.wp-materialize.json` manifest. See `conf
1. `wp` CLI must be installed and available in PATH for `apply`. 1. `wp` CLI must be installed and available in PATH for `apply`.
2. `local` does not require `wp`. 2. `local` does not require `wp`.
3. `pandoc` must be installed and available in PATH when using `renderer: "pandoc"`.
Install dependencies: Install dependencies:

View File

@@ -11,7 +11,7 @@ Top-level fields:
2. `repo_storage_dir` (string, required) 2. `repo_storage_dir` (string, required)
Directory where git repositories are cloned or updated. Directory where git repositories are cloned or updated.
3. `renderer` (string, optional) 3. `renderer` (string, optional)
Markdown renderer to use. Allowed values: `default`, `py-gfm`. Markdown renderer to use. Allowed values: `default`, `py-gfm`, `pandoc`.
4. `git_repositories` (array, optional) 4. `git_repositories` (array, optional)
List of git repositories to manage. Default is an empty list. List of git repositories to manage. Default is an empty list.
5. `directories` (array, optional) 5. `directories` (array, optional)
@@ -53,7 +53,7 @@ Top-level fields:
3. `author` (object, optional) 3. `author` (object, optional)
Inherited author for this directory and its children. Must resolve to a single author. Inherited author for this directory and its children. Must resolve to a single author.
4. `renderer` (string, optional) 4. `renderer` (string, optional)
Markdown renderer to use for this directory. Allowed values: `default`, `py-gfm`. Markdown renderer to use for this directory. Allowed values: `default`, `py-gfm`, `pandoc`.
If omitted, it inherits from the parent scope. If omitted, it inherits from the parent scope.
5. `subdirectories` (object, optional) 5. `subdirectories` (object, optional)
Explicit list of subdirectories to traverse. Explicit list of subdirectories to traverse.
@@ -80,6 +80,11 @@ The `renderer` field inherits implicitly: if omitted, the renderer is inherited
from the parent scope; if specified, it overrides the parent without an explicit from the parent scope; if specified, it overrides the parent without an explicit
`inherit` flag. `inherit` flag.
Renderer dependencies:
1. `default` uses the Python `Markdown` library.
2. `py-gfm` requires the `py_gfm` package (imported as `mdx_gfm`).
3. `pandoc` requires the `pandoc` binary to be available on PATH.
`files` entries: `files` entries:
Each key is a Markdown file name (relative to the manifest directory). Each key is a Markdown file name (relative to the manifest directory).
@@ -95,7 +100,7 @@ Each value is an object with the following fields:
4. `last_modified` (string, optional) 4. `last_modified` (string, optional)
Manual override for the post modified time in `YYYY-MM-DD hh:mm` format. Manual override for the post modified time in `YYYY-MM-DD hh:mm` format.
5. `renderer` (string, optional) 5. `renderer` (string, optional)
Markdown renderer to use for this file. Allowed values: `default`, `py-gfm`. Markdown renderer to use for this file. Allowed values: `default`, `py-gfm`, `pandoc`.
If omitted, it inherits from the parent scope. If omitted, it inherits from the parent scope.
6. `categories` (object, optional) 6. `categories` (object, optional)
Overrides categories for this file. Uses the same `content` and `inherit` fields Overrides categories for this file. Uses the same `content` and `inherit` fields

View File

@@ -11,7 +11,7 @@ Root directory manifest (`.wp-materialize.json`):
"categories": { "content": ["Systems", "Infrastructure"], "inherit": true }, "categories": { "content": ["Systems", "Infrastructure"], "inherit": true },
"tags": { "content": ["automation", "wordpress"], "inherit": true }, "tags": { "content": ["automation", "wordpress"], "inherit": true },
"author": { "content": ["editorial"], "inherit": true }, "author": { "content": ["editorial"], "inherit": true },
"renderer": "py-gfm", "renderer": "pandoc",
"subdirectories": { "content": ["design", "notes"], "inherit": true }, "subdirectories": { "content": ["design", "notes"], "inherit": true },
"files": { "files": {
"post.md": { "post.md": {
@@ -21,7 +21,7 @@ Root directory manifest (`.wp-materialize.json`):
}, },
"essay.md": { "essay.md": {
"use_heading_as_title": { "level": 1, "strict": true }, "use_heading_as_title": { "level": 1, "strict": true },
"renderer": "default", "renderer": "py-gfm",
"created_on": "2025-01-10 09:30", "created_on": "2025-01-10 09:30",
"last_modified": "2025-02-14 16:45" "last_modified": "2025-02-14 16:45"
} }

View File

@@ -113,6 +113,6 @@ def _require_renderer(value: object, context: str) -> Optional[str]:
if not isinstance(value, str) or not value.strip(): if not isinstance(value, str) or not value.strip():
raise ConfigurationError(f"{context} must be a non-empty string") raise ConfigurationError(f"{context} must be a non-empty string")
renderer = value.strip() renderer = value.strip()
if renderer not in {"default", "py-gfm"}: if renderer not in {"default", "py-gfm", "pandoc"}:
raise ConfigurationError(f"{context} must be one of: default, py-gfm") raise ConfigurationError(f"{context} must be one of: default, py-gfm, pandoc")
return renderer return renderer

View File

@@ -174,7 +174,7 @@ def _parse_renderer_field(value: object, issues: list[ValidationIssue], context:
issues.append(ValidationIssue("Must be a non-empty string", context=context)) issues.append(ValidationIssue("Must be a non-empty string", context=context))
return None return None
renderer = value.strip() renderer = value.strip()
if renderer not in {"default", "py-gfm"}: if renderer not in {"default", "py-gfm", "pandoc"}:
issues.append(ValidationIssue("Must be one of: default, py-gfm", context=context)) issues.append(ValidationIssue("Must be one of: default, py-gfm, pandoc", context=context))
return None return None
return renderer return renderer

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
import re import re
import markdown as md_lib import markdown as md_lib
import subprocess
from .errors import ValidationIssue from .errors import ValidationIssue
@@ -81,5 +82,22 @@ def convert_markdown(
except Exception as exc: # pragma: no cover - depends on markdown internals except Exception as exc: # pragma: no cover - depends on markdown internals
issues.append(ValidationIssue(f"Markdown conversion failed: {exc}", context=context)) issues.append(ValidationIssue(f"Markdown conversion failed: {exc}", context=context))
return None return None
if renderer == "pandoc":
try:
result = subprocess.run(
["pandoc", "--from=markdown", "--to=html5"],
input=markdown_text,
text=True,
capture_output=True,
check=True,
)
return result.stdout
except FileNotFoundError as exc:
issues.append(ValidationIssue(f"pandoc is not available: {exc}", context=context))
return None
except subprocess.CalledProcessError as exc:
stderr = exc.stderr.strip() if exc.stderr else ""
issues.append(ValidationIssue(f"Pandoc conversion failed: {stderr}", context=context))
return None
issues.append(ValidationIssue(f"Unknown renderer: {renderer}", context=context)) issues.append(ValidationIssue(f"Unknown renderer: {renderer}", context=context))
return None return None