merodi
A markdown-based static site generator.
git clone https://git.pi66.xyz/merodi
About | Files | Commits | Refs | License
feat:add build modes (draft/release)
Author: pixel
Date: 2026-07-22 09:31
commit: 1b8d7d75
Stat
| A | src/api.py | 1 | +1 | -0 |
| M | src/build.py | 60 | +43 | -17 |
| M | src/config.py | 30 | +7 | -23 |
| M | src/fileops.py | 6 | +4 | -2 |
| M | src/init_project.py | 22 | +12 | -10 |
| M | src/main.py | 11 | +7 | -4 |
| M | src/modules.py | 49 | +25 | -24 |
Diff
diff --git a/src/api.py b/src/api.py
index ea920cd..a76eac0 100644
--- a/src/api.py
+++ b/src/api.py
@@ -24,6 +24,7 @@ class API:
self.globals = GlobalStore()
self.log = None
self.config = None
+ self.mode = "draft"
api = API()
diff --git a/src/build.py b/src/build.py
index 87afdef..004a822 100644
--- a/src/build.py
+++ b/src/build.py
@@ -190,7 +190,7 @@ def compile_file(md_file, html_dest, config=None, plugins=None, force: bool = Fa
hook_call("on_page_built", md_file, html_dest)
return result
...
Show all
Show less
diff --git a/src/api.py b/src/api.py
index ea920cd..a76eac0 100644
--- a/src/api.py
+++ b/src/api.py
@@ -24,6 +24,7 @@ class API:
self.globals = GlobalStore()
self.log = None
self.config = None
+ self.mode = "draft"
api = API()
diff --git a/src/build.py b/src/build.py
index 87afdef..004a822 100644
--- a/src/build.py
+++ b/src/build.py
@@ -190,7 +190,7 @@ def compile_file(md_file, html_dest, config=None, plugins=None, force: bool = Fa
hook_call("on_page_built", md_file, html_dest)
return result
...
diff --git a/src/api.py b/src/api.py
index ea920cd..a76eac0 100644
--- a/src/api.py
+++ b/src/api.py
@@ -24,6 +24,7 @@ class API:
self.globals = GlobalStore()
self.log = None
self.config = None
+ self.mode = "draft"
api = API()
diff --git a/src/build.py b/src/build.py
index 87afdef..004a822 100644
--- a/src/build.py
+++ b/src/build.py
@@ -190,7 +190,7 @@ def compile_file(md_file, html_dest, config=None, plugins=None, force: bool = Fa
hook_call("on_page_built", md_file, html_dest)
return result
-def walk_and_build(config, plugins, force_all=False):
+def walk_and_build(config, plugins, dest, force_all=False):
md_path = config.tree.markdown
hook_call("on_walk_start", config)
files = []
@@ -201,16 +201,35 @@ def walk_and_build(config, plugins, force_all=False):
hook_call("on_walk_file", md_file)
md_relpath = path.relpath(md_file, md_path)
if is_dotfile(md_relpath): continue
- html_dest = path.join(config.tree.dest, md_relpath)
+ html_dest = path.join(dest, md_relpath)
if html_dest.endswith(".md"):
html_dest = html_dest.removesuffix(".md") + ".html"
source_file = readlink(md_file) if path.islink(md_file) else md_file
compile_file(source_file, html_dest, config, plugins, force=force_all)
hook_call("on_walk_end", config)
-def build_all(config, plugins):
+def build_all(config, plugins, dest):
clear_all_hashes(config)
- walk_and_build(config, plugins, force_all=True)
+ walk_and_build(config, plugins, dest, force_all=True)
+
+def validate_build(config, plugins):
+ errors = []
+ md_path = config.tree.markdown
+ for parent, _, filenames in walk(md_path):
+ for filename in filenames:
+ md_file = path.join(parent, filename)
+ if is_dotfile(path.relpath(md_file, md_path)):
+ continue
+ source = readlink(md_file) if path.islink(md_file) else md_file
+ try:
+ compile_page(read_md_content(source), config=config, plugins=plugins)
+ except Exception as e:
+ errors.append(f"{path.relpath(md_file, md_path)}: {e}")
+ if errors:
+ for e in errors:
+ warn(e)
+ raise Exception(f"Validation failed with {len(errors)} error(s)")
+ info("Validation passed.")
def build_if_file(project_path, file):
if project_path:
@@ -220,20 +239,27 @@ def build_if_file(project_path, file):
else:
compile_file(file[0], file[1])
-def build(building_type: str, project_path: str, file: list[str]):
+def build(mode, project_path, file, validate=False, force=False):
try:
if file:
- build_if_file(project_path, file)
- else:
- project_path = project_path if project_path else getcwd()
- find_project_from_path(project_path)
- chdir(project_path)
- config = load_config()
- plugins = load_plugins(config)
- hook_call("on_build_start", config)
- walk_and_build(config, plugins)
- hook_call("on_build_end", config)
-
-
+ return build_if_file(project_path, file)
+
+ project_path = project_path or getcwd()
+ find_project_from_path(project_path)
+ chdir(project_path)
+ config = load_config()
+ plugins = load_plugins(config)
+ dest = config.tree.draft_dest if mode == "draft" else config.tree.release_dest
+
+ if validate:
+ validate_build(config, plugins)
+ if mode == "release":
+ import shutil
+ if path.exists(dest):
+ shutil.rmtree(dest)
+
+ hook_call("on_build_start", config)
+ walk_and_build(config, plugins, dest, force_all=force)
+ hook_call("on_build_end", config)
except Exception as e:
fatal(e, f"Build failed: {e}")
diff --git a/src/config.py b/src/config.py
index 63ee3be..c5233e9 100644
--- a/src/config.py
+++ b/src/config.py
@@ -22,30 +22,14 @@ def load_project_config(config) -> Project:
)
def load_tree_config(config) -> Tree:
- markdown = config["tree"].get("markdown", "src/md")
- static = config["tree"].get("static", "src/static")
- templates = config["tree"].get("templates", "src/templates")
- dest = config["tree"].get("dest", "src/dest")
- plugins = config["tree"].get("plugins", "src/plugins.py")
+ t = config["tree"]
+ markdown, static, templates = t["markdown"], t["static"], t["templates"]
+ draft_dest, release_dest, plugins = t["draft_dest"], t["release_dest"], t["plugins"]
+ for name, p in [("markdown", markdown), ("static", static), ("templates", templates), ("plugins", plugins)]:
+ if not exists(p):
+ raise FileNotFoundError(f"{name} directory does not exist: {p}")
+ return Tree(markdown=markdown, static=static, templates=templates, draft_dest=draft_dest, release_dest=release_dest, plugins=plugins)
- if not exists(markdown):
- raise FileNotFoundError(f"markdown directory does not exist: {markdown}")
- if not exists(static):
- raise FileNotFoundError(f"static directory does not exist: {static}")
- if not exists(templates):
- raise FileNotFoundError(f"templates directory does not exist: {templates}")
- if not exists(plugins):
- raise FileNotFoundError(f"plugins file does not exist: {plugins}")
- if not exists(dest):
- makedirs(dest)
-
- return Tree(
- markdown = markdown,
- static = static,
- templates = templates,
- dest = dest,
- plugins = plugins,
- )
def load_webview_config(config) -> Webview:
return Webview(
diff --git a/src/fileops.py b/src/fileops.py
index 396870f..0cad009 100644
--- a/src/fileops.py
+++ b/src/fileops.py
@@ -19,7 +19,8 @@ def resolve_tree_paths(project_dir: str, tree: Tree) -> Tree:
markdown=path.join(project_dir, tree.markdown),
static=path.join(project_dir, tree.static),
templates=path.join(project_dir, tree.templates),
- dest=path.join(project_dir, tree.dest),
+ draft_dest=path.join(project_dir, tree.draft_dest),
+ release_dest=path.join(project_dir, tree.release_dest),
plugins=path.join(project_dir, tree.plugins),
)
@@ -28,5 +29,6 @@ def create_tree_dirs (tree:Tree) -> None:
makedirs(tree.markdown , exist_ok=True)
makedirs(tree.static , exist_ok=True)
makedirs(tree.templates, exist_ok=True)
- makedirs(tree.dest , exist_ok=True)
+ makedirs(tree.draft_dest , exist_ok=True)
+ makedirs(tree.release_dest , exist_ok=True)
makedirs(tree.plugins , exist_ok=True)
diff --git a/src/init_project.py b/src/init_project.py
index 62f3ba9..2ac7973 100644
--- a/src/init_project.py
+++ b/src/init_project.py
@@ -15,11 +15,12 @@ def init_config_struct(project_name:str) -> Config:
description = "Add your description here",
),
tree = Tree(
- markdown = "src/md",
- static = "src/static",
- templates = "src/templates",
- dest = "src/dest",
- plugins = "src/plugins",
+ markdown = "src/md",
+ static = "src/static",
+ templates = "src/templates",
+ draft_dest = "build/draft",
+ release_dest = "build/release",
+ plugins = "src/plugins",
),
webview = Webview(
host = "localhost",
@@ -43,11 +44,12 @@ version = "{config.project.version}"
description = "{config.project.description}"
[tree]
-markdown = "{config.tree.markdown}"
-static = "{config.tree.static}"
-templates = "{config.tree.templates}"
-dest = "{config.tree.dest}"
-plugins = "{config.tree.plugins}"
+markdown = "{config.tree.markdown}"
+static = "{config.tree.static}"
+templates = "{config.tree.templates}"
+draft_dest = "{config.tree.draft_dest}"
+release_dest = "{config.tree.release_dest}"
+plugins = "{config.tree.plugins}"
[webview]
host = "{config.webview.host}"
diff --git a/src/main.py b/src/main.py
index 73b78a1..36f110c 100755
--- a/src/main.py
+++ b/src/main.py
@@ -27,8 +27,9 @@ def main():
webview_parser.add_argument("path", nargs="?")
build_parser = sub_parser.add_parser("build", parents=[common])
- build_parser.add_argument("--release", action="store_true")
- build_parser.add_argument("--debug", action="store_true")
+ build_mode = build_parser.add_mutually_exclusive_group()
+ build_mode.add_argument("--draft", action="store_true", default=True)
+ build_mode.add_argument("--release", action="store_true")
build_parser.add_argument("path", nargs="?")
build_parser.add_argument("--file", nargs=2, metavar=("SRC", "DEST"))
@@ -53,8 +54,11 @@ def main():
init(project_path = args.path)
elif args.command == "build":
+ from .api import api
+ mode = "release" if args.release else "draft"
+ api.mode = mode
from .build import build
- build(building_type = "release" if args.release else "debug", project_path = args.path, file=args.file)
+ build(mode = mode, project_path = args.path, file=args.file)
elif args.command == "webview":
from .webviewer import run
@@ -65,4 +69,3 @@ def main():
run_watcher(args.path)
atexit.register(lambda: hook_call("on_end"))
-
diff --git a/src/modules.py b/src/modules.py
index 9e8b61c..8267fc1 100644
--- a/src/modules.py
+++ b/src/modules.py
@@ -1,39 +1,40 @@
from dataclasses import dataclass
-@dataclass()
+@dataclass
class Project:
- name : str
- version : str
- description : str
+ name: str
+ version: str
+ description: str
-@dataclass()
+@dataclass
class Tree:
- markdown : str
- static : str
- templates : str
- dest : str
- plugins : str
+ markdown: str
+ static: str
+ templates: str
+ draft_dest: str
+ release_dest: str
+ plugins: str
-@dataclass()
+@dataclass
class Webview:
- host : str
- port : int
- dev_tools : str
- html_path : str
- static_path : str
+ host: str
+ port: int
+ dev_tools: str
+ html_path: str
+ static_path: str
@dataclass
class Extras:
- highlight :str
+ highlight: str
@dataclass
class Cache:
- hash :str
+ hash: str
-@dataclass()
+@dataclass
class Config:
- project:Project
- tree:Tree
- webview:Webview
- extras:Extras
- cache:Cache
+ project: Project
+ tree: Tree
+ webview: Webview
+ extras: Extras
+ cache: Cache