added renderer selection (py-gfm as alternative)
This commit is contained in:
@@ -135,6 +135,7 @@ Each managed directory must contain a `.wp-materialize.json` manifest. See `conf
|
|||||||
1. Python 3.10+
|
1. Python 3.10+
|
||||||
2. Packages:
|
2. Packages:
|
||||||
- `Markdown>=3.6`
|
- `Markdown>=3.6`
|
||||||
|
- `py_gfm` (only required when using `renderer: "py-gfm"`)
|
||||||
|
|
||||||
## System Prerequisites
|
## System Prerequisites
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ Top-level fields:
|
|||||||
Path to the WordPress root directory where the `wp` CLI is executed.
|
Path to the WordPress root directory where the `wp` CLI is executed.
|
||||||
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. `git_repositories` (array, optional)
|
3. `renderer` (string, optional)
|
||||||
|
Markdown renderer to use. Allowed values: `default`, `py-gfm`.
|
||||||
|
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.
|
||||||
4. `directories` (array, optional)
|
5. `directories` (array, optional)
|
||||||
List of non-git directories to manage. Default is an empty list.
|
List of non-git directories to manage. Default is an empty list.
|
||||||
|
|
||||||
`git_repositories` entries:
|
`git_repositories` entries:
|
||||||
@@ -50,9 +52,12 @@ Top-level fields:
|
|||||||
Inherited tags for this directory and its children.
|
Inherited tags for this directory and its children.
|
||||||
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. `subdirectories` (object, optional)
|
4. `renderer` (string, optional)
|
||||||
|
Markdown renderer to use for this directory. Allowed values: `default`, `py-gfm`.
|
||||||
|
If omitted, it inherits from the parent scope.
|
||||||
|
5. `subdirectories` (object, optional)
|
||||||
Explicit list of subdirectories to traverse.
|
Explicit list of subdirectories to traverse.
|
||||||
5. `files` (object, optional)
|
6. `files` (object, optional)
|
||||||
Mapping of Markdown file names to file-level configuration.
|
Mapping of Markdown file names to file-level configuration.
|
||||||
|
|
||||||
`categories`, `tags`, `author`, and `subdirectories` objects:
|
`categories`, `tags`, `author`, and `subdirectories` objects:
|
||||||
@@ -61,7 +66,8 @@ Top-level fields:
|
|||||||
List of values for the given field.
|
List of values for the given field.
|
||||||
For `categories`, each string is a hierarchical path such as `Systems/Infrastructure`.
|
For `categories`, each string is a hierarchical path such as `Systems/Infrastructure`.
|
||||||
For `subdirectories`, each string is a directory name under the current directory.
|
For `subdirectories`, each string is a directory name under the current directory.
|
||||||
For `author`, exactly one string must remain after inheritance is applied.
|
For `author`, exactly one string must remain after inheritance is applied and it should be
|
||||||
|
a WordPress user ID (integer as a string).
|
||||||
2. `inherit` (boolean, optional, default `true`)
|
2. `inherit` (boolean, optional, default `true`)
|
||||||
If `true`, append to the parent effective list.
|
If `true`, append to the parent effective list.
|
||||||
If `false`, replace the parent list entirely.
|
If `false`, replace the parent list entirely.
|
||||||
@@ -70,6 +76,10 @@ Note: Root directory manifests do not need to specify `inherit` for these top-le
|
|||||||
fields (the default is `true`). File-level overrides inside `files` still support
|
fields (the default is `true`). File-level overrides inside `files` still support
|
||||||
inheritance via their own `inherit` fields.
|
inheritance via their own `inherit` fields.
|
||||||
|
|
||||||
|
The `renderer` field inherits implicitly: if omitted, the renderer is inherited
|
||||||
|
from the parent scope; if specified, it overrides the parent without an explicit
|
||||||
|
`inherit` flag.
|
||||||
|
|
||||||
`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).
|
||||||
@@ -84,10 +94,13 @@ Each value is an object with the following fields:
|
|||||||
Manual override for the post creation time in `YYYY-MM-DD hh:mm` format.
|
Manual override for the post creation time in `YYYY-MM-DD hh:mm` format.
|
||||||
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. `categories` (object, optional)
|
5. `renderer` (string, optional)
|
||||||
|
Markdown renderer to use for this file. Allowed values: `default`, `py-gfm`.
|
||||||
|
If omitted, it inherits from the parent scope.
|
||||||
|
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
|
||||||
as the top-level `categories` object.
|
as the top-level `categories` object.
|
||||||
6. `tags` (object, optional)
|
7. `tags` (object, optional)
|
||||||
Overrides tags for this file. Uses the same `content` and `inherit` fields
|
Overrides tags for this file. Uses the same `content` and `inherit` fields
|
||||||
as the top-level `tags` object.
|
as the top-level `tags` object.
|
||||||
|
|
||||||
@@ -102,6 +115,7 @@ If `created_on` or `last_modified` is not provided, the system infers the value.
|
|||||||
For `git_repositories` sources it uses git commit timestamps; for `directories`
|
For `git_repositories` sources it uses git commit timestamps; for `directories`
|
||||||
sources it uses filesystem timestamps. The system does not auto-detect git for
|
sources it uses filesystem timestamps. The system does not auto-detect git for
|
||||||
entries declared under `directories`, even if the path is inside a git repo.
|
entries declared under `directories`, even if the path is inside a git repo.
|
||||||
|
If `created_on` is in the future, WordPress will mark the post as scheduled.
|
||||||
|
|
||||||
## Post Identity
|
## Post Identity
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +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",
|
||||||
"subdirectories": { "content": ["design", "notes"], "inherit": true },
|
"subdirectories": { "content": ["design", "notes"], "inherit": true },
|
||||||
"files": {
|
"files": {
|
||||||
"post.md": {
|
"post.md": {
|
||||||
@@ -20,6 +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",
|
||||||
"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"
|
||||||
}
|
}
|
||||||
@@ -48,6 +50,7 @@ Subdirectory manifest (`design/.wp-materialize.json`):
|
|||||||
{
|
{
|
||||||
"wordpress_root": "/var/www/wordpress",
|
"wordpress_root": "/var/www/wordpress",
|
||||||
"repo_storage_dir": "/home/user/wp-materialize-repos",
|
"repo_storage_dir": "/home/user/wp-materialize-repos",
|
||||||
|
"renderer": "default",
|
||||||
"git_repositories": [],
|
"git_repositories": [],
|
||||||
"directories": [
|
"directories": [
|
||||||
{
|
{
|
||||||
@@ -65,6 +68,7 @@ Subdirectory manifest (`design/.wp-materialize.json`):
|
|||||||
{
|
{
|
||||||
"wordpress_root": "/var/www/wordpress",
|
"wordpress_root": "/var/www/wordpress",
|
||||||
"repo_storage_dir": "/home/user/wp-materialize-repos",
|
"repo_storage_dir": "/home/user/wp-materialize-repos",
|
||||||
|
"renderer": "default",
|
||||||
"git_repositories": [
|
"git_repositories": [
|
||||||
{
|
{
|
||||||
"name": "content-repo",
|
"name": "content-repo",
|
||||||
@@ -95,6 +99,7 @@ Subdirectory manifest (`design/.wp-materialize.json`):
|
|||||||
{
|
{
|
||||||
"wordpress_root": "/var/www/wordpress",
|
"wordpress_root": "/var/www/wordpress",
|
||||||
"repo_storage_dir": "/home/user/wp-materialize-repos",
|
"repo_storage_dir": "/home/user/wp-materialize-repos",
|
||||||
|
"renderer": "default",
|
||||||
"git_repositories": [
|
"git_repositories": [
|
||||||
{
|
{
|
||||||
"name": "content-repo",
|
"name": "content-repo",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ readme = "README.md"
|
|||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Markdown>=3.6",
|
"Markdown>=3.6",
|
||||||
|
"py_gfm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
Markdown>=3.6
|
Markdown>=3.6
|
||||||
|
py_gfm
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ class Config:
|
|||||||
repo_storage_dir: Path
|
repo_storage_dir: Path
|
||||||
git_repositories: List[GitRepository]
|
git_repositories: List[GitRepository]
|
||||||
directories: List[DirectorySpec]
|
directories: List[DirectorySpec]
|
||||||
|
renderer: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
def _expect_keys(obj: dict, allowed: set[str], context: str) -> None:
|
def _expect_keys(obj: dict, allowed: set[str], context: str) -> None:
|
||||||
@@ -48,10 +49,11 @@ def load_config(path: Path) -> Config:
|
|||||||
if not isinstance(data, dict):
|
if not isinstance(data, dict):
|
||||||
raise ConfigurationError("Config must be a JSON object")
|
raise ConfigurationError("Config must be a JSON object")
|
||||||
|
|
||||||
_expect_keys(data, {"wordpress_root", "repo_storage_dir", "git_repositories", "directories"}, "config")
|
_expect_keys(data, {"wordpress_root", "repo_storage_dir", "git_repositories", "directories", "renderer"}, "config")
|
||||||
|
|
||||||
wordpress_root = _require_path(data, "wordpress_root", required=True)
|
wordpress_root = _require_path(data, "wordpress_root", required=True)
|
||||||
repo_storage_dir = _require_path(data, "repo_storage_dir", required=True)
|
repo_storage_dir = _require_path(data, "repo_storage_dir", required=True)
|
||||||
|
renderer = _require_renderer(data.get("renderer"), context="config.renderer")
|
||||||
|
|
||||||
git_repositories = []
|
git_repositories = []
|
||||||
for idx, repo in enumerate(data.get("git_repositories", []) or []):
|
for idx, repo in enumerate(data.get("git_repositories", []) or []):
|
||||||
@@ -85,6 +87,7 @@ def load_config(path: Path) -> Config:
|
|||||||
repo_storage_dir=repo_storage_dir,
|
repo_storage_dir=repo_storage_dir,
|
||||||
git_repositories=git_repositories,
|
git_repositories=git_repositories,
|
||||||
directories=directories,
|
directories=directories,
|
||||||
|
renderer=renderer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -102,3 +105,14 @@ def _require_path(data: dict, key: str, required: bool) -> Path:
|
|||||||
if not isinstance(value, str) or not value.strip():
|
if not isinstance(value, str) or not value.strip():
|
||||||
raise ConfigurationError(f"{key} must be a non-empty string")
|
raise ConfigurationError(f"{key} must be a non-empty string")
|
||||||
return Path(value)
|
return Path(value)
|
||||||
|
|
||||||
|
|
||||||
|
def _require_renderer(value: object, context: str) -> Optional[str]:
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
if not isinstance(value, str) or not value.strip():
|
||||||
|
raise ConfigurationError(f"{context} must be a non-empty string")
|
||||||
|
renderer = value.strip()
|
||||||
|
if renderer not in {"default", "py-gfm"}:
|
||||||
|
raise ConfigurationError(f"{context} must be one of: default, py-gfm")
|
||||||
|
return renderer
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class _Context:
|
|||||||
categories: InheritList
|
categories: InheritList
|
||||||
tags: InheritList
|
tags: InheritList
|
||||||
author: InheritList
|
author: InheritList
|
||||||
|
renderer: Optional[str]
|
||||||
subdirectories: InheritList
|
subdirectories: InheritList
|
||||||
manifest_chain: List[Path]
|
manifest_chain: List[Path]
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ def evaluate(
|
|||||||
categories=InheritList(),
|
categories=InheritList(),
|
||||||
tags=InheritList(),
|
tags=InheritList(),
|
||||||
author=InheritList(),
|
author=InheritList(),
|
||||||
|
renderer=config.renderer,
|
||||||
subdirectories=InheritList(),
|
subdirectories=InheritList(),
|
||||||
manifest_chain=[],
|
manifest_chain=[],
|
||||||
),
|
),
|
||||||
@@ -141,6 +143,7 @@ def _evaluate_directory(
|
|||||||
effective_categories = _merge_inherit(context.categories, manifest.categories)
|
effective_categories = _merge_inherit(context.categories, manifest.categories)
|
||||||
effective_tags = _merge_inherit(context.tags, manifest.tags)
|
effective_tags = _merge_inherit(context.tags, manifest.tags)
|
||||||
effective_author = _merge_inherit(context.author, manifest.author)
|
effective_author = _merge_inherit(context.author, manifest.author)
|
||||||
|
effective_renderer = manifest.renderer if manifest.renderer is not None else context.renderer
|
||||||
effective_subdirs = _merge_inherit(context.subdirectories, manifest.subdirectories)
|
effective_subdirs = _merge_inherit(context.subdirectories, manifest.subdirectories)
|
||||||
|
|
||||||
manifest_chain = context.manifest_chain + [manifest.path]
|
manifest_chain = context.manifest_chain + [manifest.path]
|
||||||
@@ -181,7 +184,13 @@ def _evaluate_directory(
|
|||||||
resolved_tags = _normalize_list(resolved_tags, "tag", str(file_path), issues)
|
resolved_tags = _normalize_list(resolved_tags, "tag", str(file_path), issues)
|
||||||
resolved_author = _resolve_author(effective_author.content, str(file_path), issues)
|
resolved_author = _resolve_author(effective_author.content, str(file_path), issues)
|
||||||
|
|
||||||
html = convert_markdown(markdown_body, context=str(file_path), issues=issues)
|
resolved_renderer = spec.renderer if spec.renderer is not None else effective_renderer
|
||||||
|
html = convert_markdown(
|
||||||
|
markdown_body,
|
||||||
|
context=str(file_path),
|
||||||
|
issues=issues,
|
||||||
|
renderer=resolved_renderer or "default",
|
||||||
|
)
|
||||||
if html is None:
|
if html is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -248,6 +257,7 @@ def _evaluate_directory(
|
|||||||
categories=effective_categories,
|
categories=effective_categories,
|
||||||
tags=effective_tags,
|
tags=effective_tags,
|
||||||
author=effective_author,
|
author=effective_author,
|
||||||
|
renderer=effective_renderer,
|
||||||
subdirectories=effective_subdirs,
|
subdirectories=effective_subdirs,
|
||||||
manifest_chain=manifest_chain,
|
manifest_chain=manifest_chain,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ def load_manifest(path: Path, issues: list[ValidationIssue]) -> Manifest | None:
|
|||||||
issues.append(ValidationIssue("Manifest must be a JSON object", context=str(path)))
|
issues.append(ValidationIssue("Manifest must be a JSON object", context=str(path)))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
allowed = {"categories", "tags", "author", "subdirectories", "files"}
|
allowed = {"categories", "tags", "author", "renderer", "subdirectories", "files"}
|
||||||
extra = set(data.keys()) - allowed
|
extra = set(data.keys()) - allowed
|
||||||
if extra:
|
if extra:
|
||||||
issues.append(ValidationIssue(f"Unexpected keys: {sorted(extra)}", context=str(path)))
|
issues.append(ValidationIssue(f"Unexpected keys: {sorted(extra)}", context=str(path)))
|
||||||
@@ -33,6 +33,7 @@ def load_manifest(path: Path, issues: list[ValidationIssue]) -> Manifest | None:
|
|||||||
categories = _parse_inherit_list(data.get("categories"), issues, f"{path}:categories")
|
categories = _parse_inherit_list(data.get("categories"), issues, f"{path}:categories")
|
||||||
tags = _parse_inherit_list(data.get("tags"), issues, f"{path}:tags")
|
tags = _parse_inherit_list(data.get("tags"), issues, f"{path}:tags")
|
||||||
author = _parse_inherit_list(data.get("author"), issues, f"{path}:author")
|
author = _parse_inherit_list(data.get("author"), issues, f"{path}:author")
|
||||||
|
renderer = _parse_renderer_field(data.get("renderer"), issues, f"{path}:renderer")
|
||||||
subdirectories = _parse_inherit_list(data.get("subdirectories"), issues, f"{path}:subdirectories")
|
subdirectories = _parse_inherit_list(data.get("subdirectories"), issues, f"{path}:subdirectories")
|
||||||
|
|
||||||
files: Dict[str, FileSpec] = {}
|
files: Dict[str, FileSpec] = {}
|
||||||
@@ -48,7 +49,15 @@ def load_manifest(path: Path, issues: list[ValidationIssue]) -> Manifest | None:
|
|||||||
if not isinstance(file_cfg, dict):
|
if not isinstance(file_cfg, dict):
|
||||||
issues.append(ValidationIssue(f"{file_name} must be an object", context=str(path)))
|
issues.append(ValidationIssue(f"{file_name} must be an object", context=str(path)))
|
||||||
continue
|
continue
|
||||||
extra_file = set(file_cfg.keys()) - {"title", "use_heading_as_title", "categories", "tags", "created_on", "last_modified"}
|
extra_file = set(file_cfg.keys()) - {
|
||||||
|
"title",
|
||||||
|
"use_heading_as_title",
|
||||||
|
"categories",
|
||||||
|
"tags",
|
||||||
|
"created_on",
|
||||||
|
"last_modified",
|
||||||
|
"renderer",
|
||||||
|
}
|
||||||
if extra_file:
|
if extra_file:
|
||||||
issues.append(
|
issues.append(
|
||||||
ValidationIssue(f"{file_name} has unexpected keys: {sorted(extra_file)}", context=str(path))
|
ValidationIssue(f"{file_name} has unexpected keys: {sorted(extra_file)}", context=str(path))
|
||||||
@@ -93,6 +102,7 @@ def load_manifest(path: Path, issues: list[ValidationIssue]) -> Manifest | None:
|
|||||||
tags_override = _parse_inherit_list(file_cfg.get("tags"), issues, f"{path}:{file_name}:tags")
|
tags_override = _parse_inherit_list(file_cfg.get("tags"), issues, f"{path}:{file_name}:tags")
|
||||||
created_on = _parse_datetime_field(file_cfg.get("created_on"), issues, f"{path}:{file_name}:created_on")
|
created_on = _parse_datetime_field(file_cfg.get("created_on"), issues, f"{path}:{file_name}:created_on")
|
||||||
last_modified = _parse_datetime_field(file_cfg.get("last_modified"), issues, f"{path}:{file_name}:last_modified")
|
last_modified = _parse_datetime_field(file_cfg.get("last_modified"), issues, f"{path}:{file_name}:last_modified")
|
||||||
|
renderer_override = _parse_renderer_field(file_cfg.get("renderer"), issues, f"{path}:{file_name}:renderer")
|
||||||
if created_on and last_modified and last_modified < created_on:
|
if created_on and last_modified and last_modified < created_on:
|
||||||
issues.append(
|
issues.append(
|
||||||
ValidationIssue("last_modified cannot be earlier than created_on", context=str(path))
|
ValidationIssue("last_modified cannot be earlier than created_on", context=str(path))
|
||||||
@@ -106,6 +116,7 @@ def load_manifest(path: Path, issues: list[ValidationIssue]) -> Manifest | None:
|
|||||||
tags=tags_override,
|
tags=tags_override,
|
||||||
created_on=created_on,
|
created_on=created_on,
|
||||||
last_modified=last_modified,
|
last_modified=last_modified,
|
||||||
|
renderer=renderer_override,
|
||||||
)
|
)
|
||||||
|
|
||||||
return Manifest(
|
return Manifest(
|
||||||
@@ -113,6 +124,7 @@ def load_manifest(path: Path, issues: list[ValidationIssue]) -> Manifest | None:
|
|||||||
categories=categories,
|
categories=categories,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
author=author,
|
author=author,
|
||||||
|
renderer=renderer,
|
||||||
subdirectories=subdirectories,
|
subdirectories=subdirectories,
|
||||||
files=files,
|
files=files,
|
||||||
)
|
)
|
||||||
@@ -153,3 +165,16 @@ def _parse_datetime_field(value: object, issues: list[ValidationIssue], context:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
issues.append(ValidationIssue("Invalid datetime format (expected YYYY-MM-DD hh:mm)", context=context))
|
issues.append(ValidationIssue("Invalid datetime format (expected YYYY-MM-DD hh:mm)", context=context))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_renderer_field(value: object, issues: list[ValidationIssue], context: str) -> str | None:
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
if not isinstance(value, str) or not value.strip():
|
||||||
|
issues.append(ValidationIssue("Must be a non-empty string", context=context))
|
||||||
|
return None
|
||||||
|
renderer = value.strip()
|
||||||
|
if renderer not in {"default", "py-gfm"}:
|
||||||
|
issues.append(ValidationIssue("Must be one of: default, py-gfm", context=context))
|
||||||
|
return None
|
||||||
|
return renderer
|
||||||
|
|||||||
@@ -54,9 +54,32 @@ def _promote_headings(text: str) -> str:
|
|||||||
return "\n".join(promoted_lines)
|
return "\n".join(promoted_lines)
|
||||||
|
|
||||||
|
|
||||||
def convert_markdown(markdown_text: str, context: str, issues: list[ValidationIssue]) -> str | None:
|
def convert_markdown(
|
||||||
|
markdown_text: str,
|
||||||
|
context: str,
|
||||||
|
issues: list[ValidationIssue],
|
||||||
|
renderer: str = "default",
|
||||||
|
) -> str | None:
|
||||||
|
if renderer == "default":
|
||||||
try:
|
try:
|
||||||
return md_lib.markdown(markdown_text, extensions=["extra"], output_format="html5")
|
return md_lib.markdown(markdown_text, extensions=["extra"], output_format="html5")
|
||||||
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 == "py-gfm":
|
||||||
|
try:
|
||||||
|
import mdx_gfm
|
||||||
|
except Exception as exc: # pragma: no cover - dependency missing
|
||||||
|
issues.append(ValidationIssue(f"py-gfm is not available: {exc}", context=context))
|
||||||
|
return None
|
||||||
|
extension_class = getattr(mdx_gfm, "GithubFlavoredMarkdownExtension", None)
|
||||||
|
if extension_class is None:
|
||||||
|
issues.append(ValidationIssue("py-gfm extension not found: GithubFlavoredMarkdownExtension", context=context))
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return md_lib.markdown(markdown_text, extensions=[extension_class()], output_format="html5")
|
||||||
|
except Exception as exc: # pragma: no cover - depends on markdown internals
|
||||||
|
issues.append(ValidationIssue(f"Markdown conversion failed: {exc}", context=context))
|
||||||
|
return None
|
||||||
|
issues.append(ValidationIssue(f"Unknown renderer: {renderer}", context=context))
|
||||||
|
return None
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class FileSpec:
|
|||||||
tags: Optional[InheritList]
|
tags: Optional[InheritList]
|
||||||
created_on: Optional[datetime]
|
created_on: Optional[datetime]
|
||||||
last_modified: Optional[datetime]
|
last_modified: Optional[datetime]
|
||||||
|
renderer: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -29,6 +30,7 @@ class Manifest:
|
|||||||
categories: InheritList
|
categories: InheritList
|
||||||
tags: InheritList
|
tags: InheritList
|
||||||
author: InheritList
|
author: InheritList
|
||||||
|
renderer: Optional[str]
|
||||||
subdirectories: InheritList
|
subdirectories: InheritList
|
||||||
files: Dict[str, FileSpec]
|
files: Dict[str, FileSpec]
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ def create_config(path: Path) -> None:
|
|||||||
payload = {
|
payload = {
|
||||||
"wordpress_root": "/path/to/wordpress",
|
"wordpress_root": "/path/to/wordpress",
|
||||||
"repo_storage_dir": "/path/to/repo-storage",
|
"repo_storage_dir": "/path/to/repo-storage",
|
||||||
|
"renderer": "default",
|
||||||
"git_repositories": [
|
"git_repositories": [
|
||||||
{
|
{
|
||||||
"name": "example-repo",
|
"name": "example-repo",
|
||||||
@@ -45,6 +46,7 @@ def create_manifest(directory: Path) -> Path:
|
|||||||
"categories": {"content": [], "inherit": True},
|
"categories": {"content": [], "inherit": True},
|
||||||
"tags": {"content": [], "inherit": True},
|
"tags": {"content": [], "inherit": True},
|
||||||
"author": {"content": [], "inherit": True},
|
"author": {"content": [], "inherit": True},
|
||||||
|
"renderer": "default",
|
||||||
"subdirectories": {"content": [], "inherit": True},
|
"subdirectories": {"content": [], "inherit": True},
|
||||||
"files": {},
|
"files": {},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user