import os import re from typing import Dict, List, Any from jinja2 import Environment, FileSystemLoader from lib.types import PagesDict DEFAULT_TEMPLATE_DIR = 'templates' DEFAULT_OUTPUT_DIR = 'html' def get_template_files(template_dir: str = DEFAULT_TEMPLATE_DIR) -> List[str]: """Return template filenames from the provided template directory.""" if not os.path.exists(template_dir): return [] return [ name for name in os.listdir(template_dir) if name.endswith('.html') and not name.startswith('_') ] def get_css_files(output_dir: str = DEFAULT_OUTPUT_DIR) -> List[str]: """Return CSS file paths contained in the output directory's css folder.""" css_dir = os.path.join(output_dir, 'css') if not os.path.exists(css_dir): return [] return [ os.path.join(css_dir, name) for name in os.listdir(css_dir) if name.endswith('.css') ] def get_js_files(output_dir: str = DEFAULT_OUTPUT_DIR) -> List[str]: """Return JavaScript file paths contained in the output directory's js folder.""" js_dir = os.path.join(output_dir, 'js') if not os.path.exists(js_dir): return [] return [ os.path.join(js_dir, name) for name in os.listdir(js_dir) if name.endswith('.js') ] def render_template( template_name: str, context: Dict[str, Any], template_dir: str = DEFAULT_TEMPLATE_DIR) -> str: """Render a Jinja2 template with the provided context.""" env = Environment(loader=FileSystemLoader(template_dir)) template = env.get_template(template_name) return template.render(context) def set_active_page_by_url(pages: PagesDict, page_url: str) -> None: """Mutate navigation dictionary to mark the active page.""" for key, value in pages.items(): # value is a PageEntry TypedDict value['active'] = key == page_url def minify_js(js: str) -> str: """Minify JavaScript by removing comments and redundant whitespace.""" js = re.sub(r'//.*?\n|/\*.*?\*/', '', js, flags=re.DOTALL) js = re.sub(r'\s+', ' ', js) js = re.sub(r';\s+', ';\n', js) js = re.sub(r'\n+', ' ', js) return js.strip() def minify_css(css: str) -> str: """Minify CSS by removing comments and redundant whitespace.""" css = re.sub(r'/\*.*?\*/', '', css, flags=re.DOTALL) css = re.sub(r'\s+', ' ', css) css = re.sub(r';\s+', ';\n', css) css = re.sub(r'\s+([\{\s])', r'\1', css) css = re.sub(r'\s+}', '}', css) css = re.sub(r'\n+', ' ', css) return css.strip() def minify_html(html: str) -> str: """Minify HTML by removing comments and redundant whitespace.""" html = re.sub(r'', '', html, flags=re.DOTALL) html = re.sub(r'\s+', ' ', html) html = re.sub(r'>\s+<', '><', html) html = re.sub(r'<\s+', '<', html) html = re.sub(r'\s+ None: """Minify all CSS files within the output directory.""" for css_path in get_css_files(output_dir): with open(css_path, 'r', encoding='utf-8') as handle: content = handle.read() minified = minify_css(content) with open(css_path, 'w', encoding='utf-8') as handle: handle.write(minified) def js_minifier(output_dir: str = DEFAULT_OUTPUT_DIR) -> None: """Minify all JavaScript files within the output directory.""" for js_path in get_js_files(output_dir): with open(js_path, 'r', encoding='utf-8') as handle: content = handle.read() minified = minify_js(content) with open(js_path, 'w', encoding='utf-8') as handle: handle.write(minified) __all__ = [ 'DEFAULT_OUTPUT_DIR', 'DEFAULT_TEMPLATE_DIR', 'get_template_files', 'get_css_files', 'get_js_files', 'render_template', 'set_active_page_by_url', 'minify_js', 'minify_css', 'minify_html', 'css_minifier', 'js_minifier', ]