added block html option
This commit is contained in:
@@ -14,9 +14,12 @@ Top-level fields:
|
|||||||
Markdown renderer to use. Allowed values: `default`, `py-gfm`, `pandoc`.
|
Markdown renderer to use. Allowed values: `default`, `py-gfm`, `pandoc`.
|
||||||
4. `hard_line_breaks` (boolean, optional)
|
4. `hard_line_breaks` (boolean, optional)
|
||||||
If `true`, treat single newlines as hard line breaks.
|
If `true`, treat single newlines as hard line breaks.
|
||||||
5. `git_repositories` (array, optional)
|
5. `block_html` (boolean, optional)
|
||||||
|
If `true`, wrap HTML in a single Gutenberg HTML block to preserve formatting
|
||||||
|
in the visual editor.
|
||||||
|
6. `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.
|
||||||
6. `directories` (array, optional)
|
7. `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:
|
||||||
@@ -60,9 +63,12 @@ Top-level fields:
|
|||||||
5. `hard_line_breaks` (boolean, optional)
|
5. `hard_line_breaks` (boolean, optional)
|
||||||
If `true`, treat single newlines as hard line breaks. If omitted, it inherits
|
If `true`, treat single newlines as hard line breaks. If omitted, it inherits
|
||||||
from the parent scope.
|
from the parent scope.
|
||||||
6. `subdirectories` (object, optional)
|
6. `block_html` (boolean, optional)
|
||||||
|
If `true`, wrap HTML in a single Gutenberg HTML block to preserve formatting.
|
||||||
|
If omitted, it inherits from the parent scope.
|
||||||
|
7. `subdirectories` (object, optional)
|
||||||
Explicit list of subdirectories to traverse.
|
Explicit list of subdirectories to traverse.
|
||||||
7. `files` (object, optional)
|
8. `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:
|
||||||
@@ -87,6 +93,9 @@ from the parent scope; if specified, it overrides the parent without an explicit
|
|||||||
The `hard_line_breaks` field inherits implicitly: if omitted, the value is inherited
|
The `hard_line_breaks` field inherits implicitly: if omitted, the value 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.
|
||||||
|
The `block_html` field inherits implicitly: if omitted, the value is inherited
|
||||||
|
from the parent scope; if specified, it overrides the parent without an explicit
|
||||||
|
`inherit` flag.
|
||||||
|
|
||||||
Renderer dependencies:
|
Renderer dependencies:
|
||||||
1. `default` uses the Python `Markdown` library.
|
1. `default` uses the Python `Markdown` library.
|
||||||
@@ -113,10 +122,13 @@ Each value is an object with the following fields:
|
|||||||
6. `hard_line_breaks` (boolean, optional)
|
6. `hard_line_breaks` (boolean, optional)
|
||||||
If `true`, treat single newlines as hard line breaks. If omitted, it inherits
|
If `true`, treat single newlines as hard line breaks. If omitted, it inherits
|
||||||
from the parent scope.
|
from the parent scope.
|
||||||
7. `categories` (object, optional)
|
7. `block_html` (boolean, optional)
|
||||||
|
If `true`, wrap HTML in a single Gutenberg HTML block to preserve formatting.
|
||||||
|
If omitted, it inherits from the parent scope.
|
||||||
|
8. `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.
|
||||||
8. `tags` (object, optional)
|
9. `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.
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Root directory manifest (`.wp-materialize.json`):
|
|||||||
"author": { "content": ["editorial"], "inherit": true },
|
"author": { "content": ["editorial"], "inherit": true },
|
||||||
"renderer": "pandoc",
|
"renderer": "pandoc",
|
||||||
"hard_line_breaks": true,
|
"hard_line_breaks": true,
|
||||||
|
"block_html": true,
|
||||||
"subdirectories": { "content": ["design", "notes"], "inherit": true },
|
"subdirectories": { "content": ["design", "notes"], "inherit": true },
|
||||||
"files": {
|
"files": {
|
||||||
"post.md": {
|
"post.md": {
|
||||||
@@ -24,6 +25,7 @@ Root directory manifest (`.wp-materialize.json`):
|
|||||||
"use_heading_as_title": { "level": 1, "strict": true },
|
"use_heading_as_title": { "level": 1, "strict": true },
|
||||||
"renderer": "py-gfm",
|
"renderer": "py-gfm",
|
||||||
"hard_line_breaks": false,
|
"hard_line_breaks": false,
|
||||||
|
"block_html": false,
|
||||||
"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"
|
||||||
}
|
}
|
||||||
@@ -54,6 +56,7 @@ Subdirectory manifest (`design/.wp-materialize.json`):
|
|||||||
"repo_storage_dir": "/home/user/wp-materialize-repos",
|
"repo_storage_dir": "/home/user/wp-materialize-repos",
|
||||||
"renderer": "default",
|
"renderer": "default",
|
||||||
"hard_line_breaks": false,
|
"hard_line_breaks": false,
|
||||||
|
"block_html": false,
|
||||||
"git_repositories": [],
|
"git_repositories": [],
|
||||||
"directories": [
|
"directories": [
|
||||||
{
|
{
|
||||||
@@ -73,6 +76,7 @@ Subdirectory manifest (`design/.wp-materialize.json`):
|
|||||||
"repo_storage_dir": "/home/user/wp-materialize-repos",
|
"repo_storage_dir": "/home/user/wp-materialize-repos",
|
||||||
"renderer": "default",
|
"renderer": "default",
|
||||||
"hard_line_breaks": false,
|
"hard_line_breaks": false,
|
||||||
|
"block_html": false,
|
||||||
"git_repositories": [
|
"git_repositories": [
|
||||||
{
|
{
|
||||||
"name": "content-repo",
|
"name": "content-repo",
|
||||||
@@ -105,6 +109,7 @@ Subdirectory manifest (`design/.wp-materialize.json`):
|
|||||||
"repo_storage_dir": "/home/user/wp-materialize-repos",
|
"repo_storage_dir": "/home/user/wp-materialize-repos",
|
||||||
"renderer": "default",
|
"renderer": "default",
|
||||||
"hard_line_breaks": false,
|
"hard_line_breaks": false,
|
||||||
|
"block_html": false,
|
||||||
"git_repositories": [
|
"git_repositories": [
|
||||||
{
|
{
|
||||||
"name": "content-repo",
|
"name": "content-repo",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class Config:
|
|||||||
directories: List[DirectorySpec]
|
directories: List[DirectorySpec]
|
||||||
renderer: Optional[str]
|
renderer: Optional[str]
|
||||||
hard_line_breaks: bool
|
hard_line_breaks: bool
|
||||||
|
block_html: bool
|
||||||
|
|
||||||
|
|
||||||
def _expect_keys(obj: dict, allowed: set[str], context: str) -> None:
|
def _expect_keys(obj: dict, allowed: set[str], context: str) -> None:
|
||||||
@@ -52,7 +53,15 @@ def load_config(path: Path) -> Config:
|
|||||||
|
|
||||||
_expect_keys(
|
_expect_keys(
|
||||||
data,
|
data,
|
||||||
{"wordpress_root", "repo_storage_dir", "git_repositories", "directories", "renderer", "hard_line_breaks"},
|
{
|
||||||
|
"wordpress_root",
|
||||||
|
"repo_storage_dir",
|
||||||
|
"git_repositories",
|
||||||
|
"directories",
|
||||||
|
"renderer",
|
||||||
|
"hard_line_breaks",
|
||||||
|
"block_html",
|
||||||
|
},
|
||||||
"config",
|
"config",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -60,6 +69,7 @@ def load_config(path: Path) -> Config:
|
|||||||
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")
|
renderer = _require_renderer(data.get("renderer"), context="config.renderer")
|
||||||
hard_line_breaks = _require_bool_optional(data.get("hard_line_breaks"), context="config.hard_line_breaks")
|
hard_line_breaks = _require_bool_optional(data.get("hard_line_breaks"), context="config.hard_line_breaks")
|
||||||
|
block_html = _require_bool_optional(data.get("block_html"), context="config.block_html")
|
||||||
|
|
||||||
git_repositories = []
|
git_repositories = []
|
||||||
for idx, repo in enumerate(data.get("git_repositories", []) or []):
|
for idx, repo in enumerate(data.get("git_repositories", []) or []):
|
||||||
@@ -95,6 +105,7 @@ def load_config(path: Path) -> Config:
|
|||||||
directories=directories,
|
directories=directories,
|
||||||
renderer=renderer,
|
renderer=renderer,
|
||||||
hard_line_breaks=False if hard_line_breaks is None else hard_line_breaks,
|
hard_line_breaks=False if hard_line_breaks is None else hard_line_breaks,
|
||||||
|
block_html=False if block_html is None else block_html,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class _Context:
|
|||||||
author: InheritList
|
author: InheritList
|
||||||
renderer: Optional[str]
|
renderer: Optional[str]
|
||||||
hard_line_breaks: bool
|
hard_line_breaks: bool
|
||||||
|
block_html: bool
|
||||||
subdirectories: InheritList
|
subdirectories: InheritList
|
||||||
manifest_chain: List[Path]
|
manifest_chain: List[Path]
|
||||||
|
|
||||||
@@ -49,6 +50,7 @@ def evaluate(
|
|||||||
author=InheritList(),
|
author=InheritList(),
|
||||||
renderer=config.renderer,
|
renderer=config.renderer,
|
||||||
hard_line_breaks=config.hard_line_breaks,
|
hard_line_breaks=config.hard_line_breaks,
|
||||||
|
block_html=config.block_html,
|
||||||
subdirectories=InheritList(),
|
subdirectories=InheritList(),
|
||||||
manifest_chain=[],
|
manifest_chain=[],
|
||||||
),
|
),
|
||||||
@@ -151,6 +153,11 @@ def _evaluate_directory(
|
|||||||
if manifest.hard_line_breaks is not None
|
if manifest.hard_line_breaks is not None
|
||||||
else context.hard_line_breaks
|
else context.hard_line_breaks
|
||||||
)
|
)
|
||||||
|
effective_block_html = (
|
||||||
|
manifest.block_html
|
||||||
|
if manifest.block_html is not None
|
||||||
|
else context.block_html
|
||||||
|
)
|
||||||
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]
|
||||||
@@ -197,12 +204,18 @@ def _evaluate_directory(
|
|||||||
if spec.hard_line_breaks is not None
|
if spec.hard_line_breaks is not None
|
||||||
else effective_hard_line_breaks
|
else effective_hard_line_breaks
|
||||||
)
|
)
|
||||||
|
resolved_block_html = (
|
||||||
|
spec.block_html
|
||||||
|
if spec.block_html is not None
|
||||||
|
else effective_block_html
|
||||||
|
)
|
||||||
html = convert_markdown(
|
html = convert_markdown(
|
||||||
markdown_body,
|
markdown_body,
|
||||||
context=str(file_path),
|
context=str(file_path),
|
||||||
issues=issues,
|
issues=issues,
|
||||||
renderer=resolved_renderer or "default",
|
renderer=resolved_renderer or "default",
|
||||||
hard_line_breaks=resolved_hard_line_breaks,
|
hard_line_breaks=resolved_hard_line_breaks,
|
||||||
|
block_html=resolved_block_html,
|
||||||
)
|
)
|
||||||
if html is None:
|
if html is None:
|
||||||
continue
|
continue
|
||||||
@@ -272,6 +285,7 @@ def _evaluate_directory(
|
|||||||
author=effective_author,
|
author=effective_author,
|
||||||
renderer=effective_renderer,
|
renderer=effective_renderer,
|
||||||
hard_line_breaks=effective_hard_line_breaks,
|
hard_line_breaks=effective_hard_line_breaks,
|
||||||
|
block_html=effective_block_html,
|
||||||
subdirectories=effective_subdirs,
|
subdirectories=effective_subdirs,
|
||||||
manifest_chain=manifest_chain,
|
manifest_chain=manifest_chain,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -24,7 +24,16 @@ 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", "renderer", "hard_line_breaks", "subdirectories", "files"}
|
allowed = {
|
||||||
|
"categories",
|
||||||
|
"tags",
|
||||||
|
"author",
|
||||||
|
"renderer",
|
||||||
|
"hard_line_breaks",
|
||||||
|
"block_html",
|
||||||
|
"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)))
|
||||||
@@ -35,6 +44,7 @@ def load_manifest(path: Path, issues: list[ValidationIssue]) -> Manifest | None:
|
|||||||
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")
|
renderer = _parse_renderer_field(data.get("renderer"), issues, f"{path}:renderer")
|
||||||
hard_line_breaks = _parse_bool_field(data.get("hard_line_breaks"), issues, f"{path}:hard_line_breaks")
|
hard_line_breaks = _parse_bool_field(data.get("hard_line_breaks"), issues, f"{path}:hard_line_breaks")
|
||||||
|
block_html = _parse_bool_field(data.get("block_html"), issues, f"{path}:block_html")
|
||||||
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] = {}
|
||||||
@@ -59,6 +69,7 @@ def load_manifest(path: Path, issues: list[ValidationIssue]) -> Manifest | None:
|
|||||||
"last_modified",
|
"last_modified",
|
||||||
"renderer",
|
"renderer",
|
||||||
"hard_line_breaks",
|
"hard_line_breaks",
|
||||||
|
"block_html",
|
||||||
}
|
}
|
||||||
if extra_file:
|
if extra_file:
|
||||||
issues.append(
|
issues.append(
|
||||||
@@ -110,6 +121,11 @@ def load_manifest(path: Path, issues: list[ValidationIssue]) -> Manifest | None:
|
|||||||
issues,
|
issues,
|
||||||
f"{path}:{file_name}:hard_line_breaks",
|
f"{path}:{file_name}:hard_line_breaks",
|
||||||
)
|
)
|
||||||
|
block_html_override = _parse_bool_field(
|
||||||
|
file_cfg.get("block_html"),
|
||||||
|
issues,
|
||||||
|
f"{path}:{file_name}:block_html",
|
||||||
|
)
|
||||||
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))
|
||||||
@@ -125,6 +141,7 @@ def load_manifest(path: Path, issues: list[ValidationIssue]) -> Manifest | None:
|
|||||||
last_modified=last_modified,
|
last_modified=last_modified,
|
||||||
renderer=renderer_override,
|
renderer=renderer_override,
|
||||||
hard_line_breaks=hard_line_breaks_override,
|
hard_line_breaks=hard_line_breaks_override,
|
||||||
|
block_html=block_html_override,
|
||||||
)
|
)
|
||||||
|
|
||||||
return Manifest(
|
return Manifest(
|
||||||
@@ -134,6 +151,7 @@ def load_manifest(path: Path, issues: list[ValidationIssue]) -> Manifest | None:
|
|||||||
author=author,
|
author=author,
|
||||||
renderer=renderer,
|
renderer=renderer,
|
||||||
hard_line_breaks=hard_line_breaks,
|
hard_line_breaks=hard_line_breaks,
|
||||||
|
block_html=block_html,
|
||||||
subdirectories=subdirectories,
|
subdirectories=subdirectories,
|
||||||
files=files,
|
files=files,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -61,13 +61,19 @@ def convert_markdown(
|
|||||||
issues: list[ValidationIssue],
|
issues: list[ValidationIssue],
|
||||||
renderer: str = "default",
|
renderer: str = "default",
|
||||||
hard_line_breaks: bool = False,
|
hard_line_breaks: bool = False,
|
||||||
|
block_html: bool = False,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
|
def wrap_blocks(html: str) -> str:
|
||||||
|
if not block_html:
|
||||||
|
return html
|
||||||
|
return f\"<!-- wp:html -->\\n{html}\\n<!-- /wp:html -->\"
|
||||||
|
|
||||||
if renderer == "default":
|
if renderer == "default":
|
||||||
try:
|
try:
|
||||||
extensions = ["extra"]
|
extensions = ["extra"]
|
||||||
if hard_line_breaks:
|
if hard_line_breaks:
|
||||||
extensions.append("nl2br")
|
extensions.append("nl2br")
|
||||||
return md_lib.markdown(markdown_text, extensions=extensions, output_format="html5")
|
return wrap_blocks(md_lib.markdown(markdown_text, extensions=extensions, 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
|
||||||
@@ -85,7 +91,7 @@ def convert_markdown(
|
|||||||
extensions = [extension_class()]
|
extensions = [extension_class()]
|
||||||
if hard_line_breaks:
|
if hard_line_breaks:
|
||||||
extensions.append("nl2br")
|
extensions.append("nl2br")
|
||||||
return md_lib.markdown(markdown_text, extensions=extensions, output_format="html5")
|
return wrap_blocks(md_lib.markdown(markdown_text, extensions=extensions, 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
|
||||||
@@ -98,7 +104,7 @@ def convert_markdown(
|
|||||||
capture_output=True,
|
capture_output=True,
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
return result.stdout
|
return wrap_blocks(result.stdout)
|
||||||
except FileNotFoundError as exc:
|
except FileNotFoundError as exc:
|
||||||
issues.append(ValidationIssue(f"pandoc is not available: {exc}", context=context))
|
issues.append(ValidationIssue(f"pandoc is not available: {exc}", context=context))
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class FileSpec:
|
|||||||
last_modified: Optional[datetime]
|
last_modified: Optional[datetime]
|
||||||
renderer: Optional[str]
|
renderer: Optional[str]
|
||||||
hard_line_breaks: Optional[bool]
|
hard_line_breaks: Optional[bool]
|
||||||
|
block_html: Optional[bool]
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -33,6 +34,7 @@ class Manifest:
|
|||||||
author: InheritList
|
author: InheritList
|
||||||
renderer: Optional[str]
|
renderer: Optional[str]
|
||||||
hard_line_breaks: Optional[bool]
|
hard_line_breaks: Optional[bool]
|
||||||
|
block_html: Optional[bool]
|
||||||
subdirectories: InheritList
|
subdirectories: InheritList
|
||||||
files: Dict[str, FileSpec]
|
files: Dict[str, FileSpec]
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ def create_config(path: Path) -> None:
|
|||||||
"repo_storage_dir": "/path/to/repo-storage",
|
"repo_storage_dir": "/path/to/repo-storage",
|
||||||
"renderer": "default",
|
"renderer": "default",
|
||||||
"hard_line_breaks": False,
|
"hard_line_breaks": False,
|
||||||
|
"block_html": False,
|
||||||
"git_repositories": [
|
"git_repositories": [
|
||||||
{
|
{
|
||||||
"name": "example-repo",
|
"name": "example-repo",
|
||||||
@@ -49,6 +50,7 @@ def create_manifest(directory: Path) -> Path:
|
|||||||
"author": {"content": [], "inherit": True},
|
"author": {"content": [], "inherit": True},
|
||||||
"renderer": "default",
|
"renderer": "default",
|
||||||
"hard_line_breaks": False,
|
"hard_line_breaks": False,
|
||||||
|
"block_html": False,
|
||||||
"subdirectories": {"content": [], "inherit": True},
|
"subdirectories": {"content": [], "inherit": True},
|
||||||
"files": {},
|
"files": {},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user