diff --git a/grimoire-ssg/__main__.py b/grimoire-ssg/__main__.py deleted file mode 100644 index b89a532..0000000 --- a/grimoire-ssg/__main__.py +++ /dev/null @@ -1,131 +0,0 @@ -import argparse -import glob -import os - -import markdown -import yaml -from jinja2 import Environment, FileSystemLoader -from yaml import Loader - -jinja_env = Environment( - loader=FileSystemLoader("/") -) - - -def to_relative(path): - trimmed = path.removeprefix(os.getcwd()) - if trimmed != path: - trimmed = "." + trimmed - return trimmed - - -def compile_markdown(data): - for entry in data: - if "markdown" in entry: - print(f"Compiling markdown for {entry['relative_filename']}...") - entry["markdown_compiled"] = markdown.markdown(entry["markdown"]) - - -def render(data, tags, output_dir): - files_written = 0 - - for entry in data: - if "template" in entry: - template_path = os.path.realpath(os.path.dirname(entry["filename"]) + "/" + entry["template"]) - template_dir = os.path.dirname(template_path) - print(f"Rendering template for {entry['relative_filename']}...") - template = jinja_env.get_template(template_path) - entry["rendered"] = template.render(current=entry, all=data, tags=tags, template_dir=template_dir) - - if "rendered" in entry and "output" in entry: - files_written += 1 - filename = os.path.realpath(output_dir + "/" + entry["output"]) - print(f" ... writing to {to_relative(filename)}") - os.makedirs(os.path.dirname(filename), exist_ok=True) - with open(filename, "w") as file: - file.write(entry["rendered"]) - - return files_written - - -def extract_tags(data): - tags = {} - - for entry in data: - for tag in entry.get("tags", []): - entry_list = tags.get(tag, []) - entry_list.append(entry) - tags[tag] = entry_list - - print(f"Found tags: " + repr(list(tags.keys()))) - - return tags - - -def handle_file_or_glob(globname): - results = [] - - for filename in glob.glob(os.path.realpath(globname)): - results.extend(handle_file(filename)) - - return results - - -def handle_file(filename): - print(f"Reading {to_relative(filename)}...") - - with open(filename, "r") as file: - data = yaml.load(file, Loader) - - data["filename"] = filename - data["relative_filename"] = to_relative(filename) - results = [data] - - relative_dir = os.path.dirname(filename) - for filename in data.get("include", []): - filename = relative_dir + "/" + filename - sub_data = handle_file_or_glob(filename) - results.extend(sub_data) - - return results - - -def parse_arguments(): - parser = argparse.ArgumentParser() - parser.add_argument("-o", "--output", default="./output/") - - args, filenames = parser.parse_known_args() - - return args.output, filenames - - -def main(): - output_dir, filenames = parse_arguments() - - print(f"Output directory: {output_dir}") - print(f"Initial filenames: {filenames}") - print() - - if len(filenames) == 0: - print("error: at least one filename needed") - exit(1) - - data = [] - for filename in filenames: - data.extend(handle_file_or_glob(filename)) - - print(f"Total number of entries: {len(data)}") - print() - - compile_markdown(data) - tags = extract_tags(data) - files_written = render(data, tags, output_dir) - - print(f"Total files written: {files_written}") - - print() - print("Done.") - - -if __name__ == "__main__": - main() diff --git a/grimoiressg/__main__.py b/grimoiressg/__main__.py new file mode 100644 index 0000000..0b4004e --- /dev/null +++ b/grimoiressg/__main__.py @@ -0,0 +1,25 @@ +from grimoiressg.arguments import parse_arguments_to_initial_context +from grimoiressg.config import read_config +from grimoiressg.content_files import recursively_read_files +from grimoiressg.modules import available_modules + + +def apply_modules(data, config, context): + for module in config.get("enabled_modules", []): + print(f"Applying module {module}...") + available_modules[module](data, context) + print("") + + +def main(): + context = parse_arguments_to_initial_context() + config = read_config(context) + + data = recursively_read_files(context) + apply_modules(data, config, context) + + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/grimoiressg/arguments.py b/grimoiressg/arguments.py new file mode 100644 index 0000000..22dd6b8 --- /dev/null +++ b/grimoiressg/arguments.py @@ -0,0 +1,31 @@ +import argparse + + +def parse_arguments_to_initial_context(): + parser = argparse.ArgumentParser( + description=''' + Grimoire is a minimalistic Static Site Generator. + In the simplest case the only argument needed is at least one content file. \ + The rest of the flags is used to customize the behavior. + ''' + ) + parser.add_argument("content_file", nargs='+', help="one or more content files") + parser.add_argument("-o", "--output", default="./output/", help="the output directory (default: ./output/)") + parser.add_argument("-c", "--config", help="the config file to use") + + args, _ = parser.parse_known_args() + + context = { + "output_dir": args.output, + "config_file": args.config, + "filenames": args.content_file + } + + print(f"Output directory: {context['output_dir']}") + print(f"Config file: {context['config_file']}") + print("Content files:") + for filename in context["filenames"]: + print(f" - {filename}") + print() + + return context diff --git a/grimoiressg/config.py b/grimoiressg/config.py new file mode 100644 index 0000000..ada4269 --- /dev/null +++ b/grimoiressg/config.py @@ -0,0 +1,35 @@ +import yaml +from yaml import Loader + +from grimoiressg.modules import available_modules + + +def default_config(): + return { + "enabled_modules": [ + "tags", + "markdown", + "templating" + ] + } + + +def read_config(context): + config_file = context.get("config_file", None) + + if not config_file: + print("No config file given; using default config") + config = default_config() + else: + with open(config_file, "r") as file: + config = yaml.load(file, Loader) or {} + + print("Enabled modules:") + for module in config.get("enabled_modules", []): + print(f" - {module}") + if module not in available_modules: + print(f" ERROR: Module does not exist") + exit(1) + print() + + return config diff --git a/grimoiressg/content_files.py b/grimoiressg/content_files.py new file mode 100644 index 0000000..0c3f299 --- /dev/null +++ b/grimoiressg/content_files.py @@ -0,0 +1,39 @@ +import os + +import yaml +from yaml import Loader + +from grimoiressg.utils.files import for_each_glob, to_relative + + +def handle_file(filename): + print(f" Reading {to_relative(filename)}...") + + with open(filename, "r") as file: + data = yaml.load(file, Loader) + + data["filename"] = filename + data["relative_filename"] = to_relative(filename) + results = [data] + + relative_dir = os.path.dirname(filename) + for filename in data.get("include", []): + filename = relative_dir + "/" + filename + sub_data = for_each_glob(filename, handle_file) + results.extend(sub_data) + + return results + + +def recursively_read_files(context): + data = [] + + print("Reading content files...") + + for filename in context["filenames"]: + data.extend(for_each_glob(filename, handle_file)) + + print(f"Read {len(data)} files in total.") + print() + + return data diff --git a/grimoiressg/modules/__init__.py b/grimoiressg/modules/__init__.py new file mode 100644 index 0000000..1240793 --- /dev/null +++ b/grimoiressg/modules/__init__.py @@ -0,0 +1,9 @@ +from grimoiressg.modules.markdown import compile_markdown +from grimoiressg.modules.tags import extract_tags +from grimoiressg.modules.templating import render_templates + +available_modules = { + "tags": extract_tags, + "markdown": compile_markdown, + "templating": render_templates +} diff --git a/grimoiressg/modules/markdown.py b/grimoiressg/modules/markdown.py new file mode 100644 index 0000000..8bbfca4 --- /dev/null +++ b/grimoiressg/modules/markdown.py @@ -0,0 +1,8 @@ +import markdown + + +def compile_markdown(data, context): + for entry in data: + if "markdown" in entry: + print(f"Compiling markdown for {entry['relative_filename']}...") + entry["markdown_compiled"] = markdown.markdown(entry["markdown"]) diff --git a/grimoiressg/modules/tags.py b/grimoiressg/modules/tags.py new file mode 100644 index 0000000..053f88c --- /dev/null +++ b/grimoiressg/modules/tags.py @@ -0,0 +1,18 @@ + +def extract_tags(data, context): + tags = {} + + for entry in data: + for tag in entry.get("tags", []): + entry_list = tags.get(tag, []) + entry_list.append(entry) + tags[tag] = entry_list + + if tags: + print("Found tags:") + for tag in tags.keys(): + print(f" - {tag} ({len(tags[tag])} files)") + else: + print("No tags found.") + + context["tags"] = tags \ No newline at end of file diff --git a/grimoiressg/modules/templating.py b/grimoiressg/modules/templating.py new file mode 100644 index 0000000..f7c875b --- /dev/null +++ b/grimoiressg/modules/templating.py @@ -0,0 +1,36 @@ +import os + +from grimoiressg.utils import to_relative + +from jinja2 import Environment, FileSystemLoader + +jinja_env = Environment( + loader=FileSystemLoader("/") +) + + +def render_templates(data, context): + files_written = 0 + + for entry in data: + if "template" in entry: + template_path = os.path.realpath(os.path.dirname(entry["filename"]) + "/" + entry["template"]) + template_dir = os.path.dirname(template_path) + print(f"Rendering template for {entry['relative_filename']}...") + template = jinja_env.get_template(template_path) + entry["rendered"] = template.render( + **context, + current=entry, + all=data, + template_dir=template_dir + ) + + if "rendered" in entry and "output" in entry: + files_written += 1 + filename = os.path.realpath(context["output_dir"] + "/" + entry["output"]) + print(f" writing to {to_relative(filename)}") + os.makedirs(os.path.dirname(filename), exist_ok=True) + with open(filename, "w") as file: + file.write(entry["rendered"]) + + print(f"{files_written} rendered") diff --git a/grimoiressg/utils/__init__.py b/grimoiressg/utils/__init__.py new file mode 100644 index 0000000..8205bd0 --- /dev/null +++ b/grimoiressg/utils/__init__.py @@ -0,0 +1 @@ +from .files import to_relative diff --git a/grimoiressg/utils/files.py b/grimoiressg/utils/files.py new file mode 100644 index 0000000..c3ccf31 --- /dev/null +++ b/grimoiressg/utils/files.py @@ -0,0 +1,18 @@ +import glob +import os + + +def to_relative(path): + trimmed = path.removeprefix(os.getcwd()) + if trimmed != path: + trimmed = "." + trimmed + return trimmed + + +def for_each_glob(glob_path, callback): + results = [] + + for filename in glob.glob(os.path.realpath(glob_path)): + results.extend(callback(filename)) + + return results diff --git a/pyproject.toml b/pyproject.toml index 85bfcfe..a5a875b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "grimoire-ssg" packages = [ - { include = "grimoire-ssg" } + { include = "grimoiressg" } ] version = "0.1.0" description = "A minimalistic Static Site Generator"