refactor: make less messy

This commit is contained in:
Stevan Freeborn
2026-07-08 08:32:28 -05:00
parent 090aacdbb8
commit c14c519293
31 changed files with 441 additions and 366 deletions
+1
View File
@@ -1,2 +1,3 @@
__pycache__/
public/
output.txt
+1 -1
View File
@@ -1 +1 @@
python3 src/main.py "docs" "/tolkien-fan-club/"
python3 -m src "docs" "/tolkien-fan-club/"
+1 -1
View File
@@ -1,2 +1,2 @@
python3 src/main.py "public" "/"
python3 -m src "public" "/"
cd public && python3 -m http.server 8888
-7
View File
@@ -1,7 +0,0 @@
<div><pre><code>This is text that _should_ remain
the **same** even with inline stuff
</code></pre></div>
<div><pre><code>This is text that _should_ remain
the **same** even with inline stuff
</code></pre></div>
View File
+3
View File
@@ -0,0 +1,3 @@
from src.cli import main
main()
+19
View File
@@ -0,0 +1,19 @@
import sys
from src.copier import copy_static
from src.generator import generate_pages
def main() -> None:
if len(sys.argv) < 3:
raise RuntimeError("Please provide destination and basepath: <destination> <base_path>")
destination_dir = sys.argv[1]
basepath = sys.argv[2]
copy_static(destination_dir)
generate_pages("content", "template.html", destination_dir, basepath)
if __name__ == "__main__":
main()
+35
View File
@@ -0,0 +1,35 @@
import shutil
from pathlib import Path
def copy_static(destination: str) -> None:
source_path = Path("static")
destination_path = Path(destination)
if not source_path.exists():
raise FileNotFoundError(f"Source directory '{source_path}' does not exist.")
if destination_path.exists():
print(f"Cleaning contents of '{destination_path}'...")
for item in destination_path.iterdir():
if item.is_dir():
shutil.rmtree(item)
else:
item.unlink()
else:
destination_path.mkdir(parents=True, exist_ok=True)
print(f"Copying files from '{source_path}' to '{destination_path}'...")
for item in source_path.rglob("*"):
relative_path = item.relative_to(source_path)
target_path = destination_path / relative_path
if item.is_dir():
target_path.mkdir(parents=True, exist_ok=True)
else:
target_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(item, target_path)
print(f"Finished copying files from '{source_path}' to '{destination_path}'")
+52
View File
@@ -0,0 +1,52 @@
from pathlib import Path
from src.markdown.to_html import markdown_to_html_node
from src.template import extract_title, render_template
def generate_pages(
dir_path_content: str, template_path: str, dest_dir_path: str, basepath: str
) -> None:
content_dir = Path(dir_path_content)
template = Path(template_path)
dest_dir = Path(dest_dir_path)
print(f"Crawling '{content_dir}' to generate HTML pages in '{dest_dir}'...")
for item in content_dir.rglob("*"):
if item.is_file() and item.suffix.lower() == ".md":
relative_path = item.relative_to(content_dir)
target_path = dest_dir / relative_path.with_suffix(".html")
generate_page(item, template, target_path, basepath)
print("Page generation complete")
def generate_page(
from_path: str | Path, template_path: str | Path, dest_path: str | Path, basepath: str
) -> None:
src = Path(from_path)
template = Path(template_path)
dest = Path(dest_path)
if not src.exists():
raise RuntimeError(f"Source file '{src}' does not exist")
if not src.is_file():
raise RuntimeError(f"Source file '{src}' is not a file")
print(f"Generating page from '{src}' to '{dest}' using '{template}'")
markdown_content = src.read_text(encoding="utf-8")
html_node = markdown_to_html_node(markdown_content)
title = extract_title(markdown_content)
html = html_node.to_html()
rendered_html = render_template(template, title, html, basepath)
dest.parent.mkdir(parents=True, exist_ok=True)
dest.write_text(rendered_html, encoding="utf-8")
print(f"Page successfully generated: '{dest}'")
-123
View File
@@ -1,123 +0,0 @@
import shutil
import sys
from pathlib import Path
from splitnodes import markdown_to_html_node
def main():
if len(sys.argv) < 3:
raise RuntimeError("Please provide destination and basepath: <destination> <base_path>")
destination_dir = sys.argv[1]
basepath = sys.argv[2]
copy_static(destination_dir)
generate_pages("content", "template.html", destination_dir, basepath)
def copy_static(destination: str) -> None:
source_path = Path("static")
destination_path = Path(destination)
if not source_path.exists():
raise FileNotFoundError(f"Source directory '{source_path}' does not exist.")
if destination_path.exists():
print(f"Cleaning contents of '{destination_path}'...")
for item in destination_path.iterdir():
if item.is_dir():
shutil.rmtree(item)
else:
item.unlink()
else:
destination_path.mkdir(parents=True, exist_ok=True)
print(f"Copying files from '{source_path}' to '{destination_path}'...")
for item in source_path.rglob("*"):
relative_path = item.relative_to(source_path)
target_path = destination_path / relative_path
if item.is_dir():
target_path.mkdir(parents=True, exist_ok=True)
else:
target_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(item, target_path)
print(f"Finished copying files from '{source_path}' to '{destination_path}'")
def generate_pages(
dir_path_content: str, template_path: str, dest_dir_path: str, basepath: str
) -> None:
content_dir = Path(dir_path_content)
template = Path(template_path)
dest_dir = Path(dest_dir_path)
print(f"Crawling '{content_dir}' to generate HTML pages in '{dest_dir}'...")
for item in content_dir.rglob("*"):
if item.is_file() and item.suffix.lower() == ".md":
relative_path = item.relative_to(content_dir)
target_path = dest_dir / relative_path.with_suffix(".html")
generate_page(item, template, target_path, basepath)
print("Page generation complete")
def generate_page(
from_path: str | Path, template_path: str | Path, dest_path: str | Path, basepath: str
) -> None:
src = Path(from_path)
template = Path(template_path)
dest = Path(dest_path)
if not src.exists():
raise RuntimeError(f"Source file '{src}' does not exist")
if not src.is_file():
raise RuntimeError(f"Source file '{src}' is not a file")
if not template.exists():
raise RuntimeError(f"Template file '{template}' does not exist")
if not template.is_file():
raise RuntimeError(f"Template file '{template}' is not a file")
print(f"Generating page from '{src}' to '{dest}' using '{template}'")
markdown_content = src.read_text(encoding="utf-8")
template_content = template.read_text(encoding="utf-8")
html_node = markdown_to_html_node(markdown_content)
title = extract_title(markdown_content)
html = html_node.to_html()
rendered_html = template_content.replace("{{ Title }}", title)
rendered_html = rendered_html.replace("{{ Content }}", html)
rendered_html = rendered_html.replace('href="/', f'href="{basepath}')
rendered_html = rendered_html.replace('src="/', f'src="{basepath}')
dest.parent.mkdir(parents=True, exist_ok=True)
dest.write_text(rendered_html, encoding="utf-8")
print(f"Page successfuly generated: 'dest'")
def extract_title(markdown: str) -> str:
heading_identifier = "# "
lines = markdown.splitlines()
for line in lines:
if line.startswith(heading_identifier):
return line.lstrip(heading_identifier)
raise RuntimeError("No heading found that can be used as title")
if __name__ == "__main__":
main()
+23
View File
@@ -0,0 +1,23 @@
from src.markdown.blocks import BlockType, block_to_block_type
from src.markdown.extract import extract_markdown_images, extract_markdown_links
from src.markdown.inlines import (
split_nodes_delimiter,
split_nodes_image,
split_nodes_link,
text_to_text_node,
)
from src.markdown.to_html import markdown_to_blocks, markdown_to_html_node, text_to_children
__all__ = [
"BlockType",
"block_to_block_type",
"extract_markdown_images",
"extract_markdown_links",
"markdown_to_blocks",
"markdown_to_html_node",
"split_nodes_delimiter",
"split_nodes_image",
"split_nodes_link",
"text_to_children",
"text_to_text_node",
]
+15 -8
View File
@@ -1,5 +1,4 @@
import re
import os
from enum import Enum
from typing import Callable
@@ -13,6 +12,7 @@ class BlockType(Enum):
ORDERED_LIST = "ordered list"
UNORDERED_LIST = "unordered list"
def block_to_block_type(block: str) -> BlockType:
if is_heading(block):
return BlockType.HEADING
@@ -23,38 +23,45 @@ def block_to_block_type(block: str) -> BlockType:
if is_quote(block):
return BlockType.QUOTE
if is_unordered(block):
if is_unordered_list(block):
return BlockType.UNORDERED_LIST
if is_ordered(block):
if is_ordered_list(block):
return BlockType.ORDERED_LIST
return BlockType.PARAGRAPH
def is_heading(block: str) -> bool:
pattern = r"^#{1,6}\s{1}?"
return has_pattern(pattern, block)
def is_code(block: str) -> bool:
pattern = rf"^```.*{os.linesep}[\s\S]*{os.linesep}```$"
pattern = r"^```.*\n[\s\S]*\n```$"
return has_pattern(pattern, block)
def is_quote(block: str) -> bool:
pattern = r"^>"
return each_line_has_pattern(lambda _: pattern, block)
def is_unordered(block: str) -> bool:
def is_unordered_list(block: str) -> bool:
pattern = r"^-\s"
return each_line_has_pattern(lambda _: pattern, block)
def is_ordered(block: str) -> bool:
def is_ordered_list(block: str) -> bool:
return each_line_has_pattern(lambda i: rf"{i + 1}\.\s", block)
def has_pattern(pattern: str, block: str) -> bool:
return re.match(pattern, block) != None
return re.match(pattern, block) is not None
def each_line_has_pattern(pattern_builder: Callable[[int], str], block: str) -> bool:
lines = block.split(os.linesep)
lines = block.split("\n")
for i in range(len(lines)):
line = lines[i]
+3 -1
View File
@@ -1,11 +1,13 @@
import re
def extract_markdown_images(text: str) -> list[tuple[str, str]]:
return extract(r"!\[(.+?)\]\((.+?)\)", text)
def extract_markdown_links(text: str) -> list[tuple[str, str]]:
return extract(r"(?<!!)\[(.+?)\]\((.+?)\)", text)
def extract(pattern: str, text: str):
return re.findall(pattern, text)
+92
View File
@@ -0,0 +1,92 @@
from typing import Callable
from src.markdown.extract import extract_markdown_images, extract_markdown_links
from src.nodes.text import TextNode, TextType
def split_nodes_delimiter(
old_nodes: list[TextNode], delimiter: str, text_type: TextType
) -> list[TextNode]:
result = []
for node in old_nodes:
if node.text_type != TextType.PLAIN:
result.append(node)
continue
parts = node.text.split(delimiter)
if len(parts) % 2 == 0:
raise RuntimeError("No matching delimiter found")
for i in range(len(parts)):
current_text = parts[i]
if i % 2 == 0:
result.append(TextNode(current_text, TextType.PLAIN))
else:
result.append(TextNode(current_text, text_type))
return result
def split_nodes_image(old_nodes: list[TextNode]) -> list[TextNode]:
return split_nodes(
old_nodes,
extracter=extract_markdown_images,
pattern_builder=lambda t: f"![{t[0]}]({t[1]})",
node_creator=lambda t: TextNode(t[0], TextType.IMAGE, t[1]),
)
def split_nodes_link(old_nodes: list[TextNode]) -> list[TextNode]:
return split_nodes(
old_nodes,
extracter=extract_markdown_links,
pattern_builder=lambda t: f"[{t[0]}]({t[1]})",
node_creator=lambda t: TextNode(t[0], TextType.LINK, t[1]),
)
def split_nodes(
old_nodes: list[TextNode],
extracter: Callable[[str], list[tuple[str, str]]],
pattern_builder: Callable[[tuple[str, str]], str],
node_creator: Callable[[tuple[str, str]], TextNode],
) -> list[TextNode]:
result = []
for node in old_nodes:
if node.text_type != TextType.PLAIN:
result.append(node)
continue
extracted = extracter(node.text)
node_text = node.text
for match in extracted:
pattern = pattern_builder(match)
parts = node_text.split(pattern, maxsplit=1)
result.append(TextNode(parts[0], TextType.PLAIN))
matched_node = node_creator(match)
result.append(matched_node)
node_text = parts[1]
if len(node_text) > 0:
result.append(TextNode(node_text, TextType.PLAIN))
return result
def text_to_text_node(text: str) -> list[TextNode]:
old_nodes = [TextNode(text, TextType.PLAIN)]
old_nodes = split_nodes_delimiter(old_nodes, "**", TextType.BOLD)
old_nodes = split_nodes_delimiter(old_nodes, "_", TextType.ITALIC)
old_nodes = split_nodes_delimiter(old_nodes, "`", TextType.CODE)
old_nodes = split_nodes_image(old_nodes)
old_nodes = split_nodes_link(old_nodes)
return old_nodes
+119
View File
@@ -0,0 +1,119 @@
from src.markdown.blocks import BlockType, block_to_block_type
from src.markdown.inlines import text_to_text_node
from src.nodes.html import HtmlNode
from src.nodes.parent import ParentNode
from src.nodes.text import TextNode, TextType, text_node_to_html_node
def markdown_to_blocks(markdown: str) -> list[str]:
blocks = markdown.split("\n\n")
trimmed = [block.strip() for block in blocks]
non_empty = [block for block in trimmed if block != ""]
return non_empty
def markdown_to_html_node(markdown: str) -> HtmlNode:
children = []
blocks = markdown_to_blocks(markdown)
for block in blocks:
block_type = block_to_block_type(block)
match block_type:
case BlockType.PARAGRAPH:
children.append(handle_paragraph(block))
case BlockType.HEADING:
children.append(handle_heading(block))
case BlockType.CODE:
children.append(handle_code(block))
case BlockType.QUOTE:
children.append(handle_quote(block))
case BlockType.ORDERED_LIST:
children.append(handle_ordered_list(block))
case BlockType.UNORDERED_LIST:
children.append(handle_unordered_list(block))
case _:
raise NotImplemented("Unknown block type")
return ParentNode("div", children=children)
def handle_paragraph(block: str) -> ParentNode:
no_newlines = block.replace("\n", " ")
paragraph_children = text_to_children(no_newlines)
return ParentNode("p", children=paragraph_children)
def handle_heading(block: str) -> ParentNode:
heading_number = 0
for c in block:
if c != "#":
break
heading_number += 1
heading_children = text_to_children(block.lstrip(f"{'#' * heading_number} "))
return ParentNode(f"h{heading_number}", children=heading_children)
def handle_code(block: str) -> ParentNode:
removed = block.lstrip("```\n").rstrip("```")
text_node = TextNode(removed, TextType.CODE)
code_node = text_node_to_html_node(text_node)
pre_children: list[HtmlNode] = [code_node]
return ParentNode("pre", children=pre_children)
def handle_quote(block: str) -> ParentNode:
replaced_blocks = []
lines = block.splitlines()
for line in lines:
replaced = line.lstrip("> ").lstrip(">")
replaced_blocks.append(replaced)
replaced_block = "\n".join(replaced_blocks)
quote_children = text_to_children(replaced_block)
return ParentNode("blockquote", children=quote_children)
def handle_ordered_list(block: str) -> ParentNode:
ordered_list_children = []
ordered_list_items = block.splitlines()
for i in range(len(ordered_list_items)):
ordered_item = ordered_list_items[i]
ordered_item_children_nodes = text_to_children(ordered_item.lstrip(f"{i + 1}. "))
ordered_item_node = ParentNode(
"li", children=ordered_item_children_nodes
)
ordered_list_children.append(ordered_item_node)
return ParentNode("ol", children=ordered_list_children)
def handle_unordered_list(block: str) -> ParentNode:
unordered_list_children = []
unordered_list_items = block.splitlines()
for unordered_item in unordered_list_items:
unordered_item_children_nodes = text_to_children(unordered_item.lstrip("- "))
unordered_item_node = ParentNode(
"li", children=unordered_item_children_nodes
)
unordered_list_children.append(unordered_item_node)
return ParentNode("ul", children=unordered_list_children)
def text_to_children(markdown: str) -> list[HtmlNode]:
children = []
text_nodes = text_to_text_node(markdown)
for text_node in text_nodes:
html_node = text_node_to_html_node(text_node)
children.append(html_node)
return children
+13
View File
@@ -0,0 +1,13 @@
from src.nodes.html import HtmlNode
from src.nodes.leaf import LeafNode
from src.nodes.parent import ParentNode
from src.nodes.text import TextNode, TextType, text_node_to_html_node
__all__ = [
"HtmlNode",
"LeafNode",
"ParentNode",
"TextNode",
"TextType",
"text_node_to_html_node",
]
+1 -1
View File
@@ -7,7 +7,7 @@ class HtmlNode:
tag: str | None = None,
value: str | None = None,
children: list["HtmlNode"] | None = None,
props: dict | None = None,
props: dict[str, str] | None = None,
) -> None:
self.tag = tag
self.value = value
+3 -3
View File
@@ -1,6 +1,6 @@
from typing import Self
from htmlnode import HtmlNode
from src.nodes.html import HtmlNode
class LeafNode(HtmlNode):
@@ -8,9 +8,9 @@ class LeafNode(HtmlNode):
self: Self,
tag: str | None,
value: str | None,
props: dict | None = None,
props: dict[str, str] | None = None,
) -> None:
super().__init__(tag, value, [], props)
super().__init__(tag, value, None, props)
def to_html(self: Self) -> str:
if self.value is None:
+3 -3
View File
@@ -1,6 +1,6 @@
from typing import Self
from htmlnode import HtmlNode
from src.nodes.html import HtmlNode
class ParentNode(HtmlNode):
@@ -9,7 +9,7 @@ class ParentNode(HtmlNode):
tag: str,
children: list[HtmlNode],
value: str | None = None,
props: dict | None = None,
props: dict[str, str] | None = None,
) -> None:
super().__init__(tag, value, children, props)
@@ -19,7 +19,7 @@ class ParentNode(HtmlNode):
if self.children is None or len(self.children) == 0:
raise ValueError
html = []
html.append(f"<{self.tag}{self.props_to_html()}>")
+3 -1
View File
@@ -1,6 +1,6 @@
from enum import Enum
from leafnode import LeafNode
from src.nodes.leaf import LeafNode
class TextType(Enum):
@@ -43,8 +43,10 @@ def text_node_to_html_node(text_node: TextNode) -> LeafNode:
case TextType.CODE:
return LeafNode("code", text_node.text)
case TextType.LINK:
assert text_node.url is not None
return LeafNode("a", text_node.text, {"href": text_node.url})
case TextType.IMAGE:
assert text_node.url is not None
return LeafNode("img", "", {"src": text_node.url, "alt": text_node.text})
case _:
raise ValueError
-197
View File
@@ -1,197 +0,0 @@
import os
from typing import Callable
from block import BlockType, block_to_block_type
from extract import extract_markdown_images, extract_markdown_links
from htmlnode import HtmlNode
from parentnode import ParentNode
from textnode import TextNode, TextType, text_node_to_html_node
def split_nodes_delimiter(
old_nodes: list[TextNode], delimiter: str, text_type: TextType
) -> list[TextNode]:
result = []
for node in old_nodes:
if node.text_type != TextType.PLAIN:
result.append(node)
continue
parts = node.text.split(delimiter)
if len(parts) % 2 == 0:
raise RuntimeError("No matching delimiter found")
for i in range(len(parts)):
current_text = parts[i]
if i % 2 == 0:
result.append(TextNode(current_text, TextType.PLAIN))
else:
result.append(TextNode(current_text, text_type))
return result
def split_nodes_image(old_nodes: list[TextNode]) -> list[TextNode]:
return split_nodes(
old_nodes,
extracter=extract_markdown_images,
pattern_builder=lambda t: f"![{t[0]}]({t[1]})",
node_creator=lambda t: TextNode(t[0], TextType.IMAGE, t[1]),
)
def split_nodes_link(old_nodes: list[TextNode]) -> list[TextNode]:
return split_nodes(
old_nodes,
extracter=extract_markdown_links,
pattern_builder=lambda t: f"[{t[0]}]({t[1]})",
node_creator=lambda t: TextNode(t[0], TextType.LINK, t[1]),
)
def split_nodes(
old_nodes: list[TextNode],
extracter: Callable[[str], list[tuple[str, str]]],
pattern_builder: Callable[[tuple[str, str]], str],
node_creator: Callable[[tuple[str, str]], TextNode],
) -> list[TextNode]:
result = []
for node in old_nodes:
if node.text_type != TextType.PLAIN:
result.append(node)
continue
extracted = extracter(node.text)
node_text = node.text
for extract in extracted:
pattern = pattern_builder(extract)
parts = node_text.split(pattern, maxsplit=1)
result.append(TextNode(parts[0], TextType.PLAIN))
node = node_creator(extract)
result.append(node)
node_text = parts[1]
if len(node_text) > 0:
result.append(TextNode(node_text, TextType.PLAIN))
return result
def text_to_text_node(text: str) -> list[TextNode]:
old_nodes = [TextNode(text, TextType.PLAIN)]
old_nodes = split_nodes_delimiter(old_nodes, "**", TextType.BOLD)
old_nodes = split_nodes_delimiter(old_nodes, "_", TextType.ITALIC)
old_nodes = split_nodes_delimiter(old_nodes, "`", TextType.CODE)
old_nodes = split_nodes_image(old_nodes)
old_nodes = split_nodes_link(old_nodes)
return old_nodes
def markdown_to_blocks(markdown: str) -> list[str]:
blocks = markdown.split(
"\n\n",
)
trimmed = [block.strip() for block in blocks]
non_empty = [block for block in trimmed if block != ""]
return non_empty
def markdown_to_html_node(markdown: str) -> HtmlNode:
children = []
blocks = markdown_to_blocks(markdown)
for block in blocks:
block_type = block_to_block_type(block)
match block_type:
case BlockType.PARAGRAPH:
no_newlines = block.replace(os.linesep, " ")
paragraph_children = text_to_children(no_newlines)
paragraph_node = ParentNode("p", children=paragraph_children)
children.append(paragraph_node)
case BlockType.HEADING:
heading_number = 0
for c in block:
if c != "#":
break
heading_number += 1
heading_children = text_to_children(block.lstrip(f"{"#" * heading_number} "))
heading_node = ParentNode(
f"h{heading_number}", children=heading_children
)
children.append(heading_node)
case BlockType.CODE:
removed = block.lstrip("```\n").rstrip("```")
text_node = TextNode(removed, TextType.CODE)
code_node = text_node_to_html_node(text_node)
pre_children: list[HtmlNode] = [code_node]
pre_node = ParentNode("pre", children=pre_children)
children.append(pre_node)
case BlockType.QUOTE:
replaced_blocks = []
lines = block.splitlines()
for line in lines:
replaced = line.lstrip("> ").lstrip(">")
replaced_blocks.append(replaced)
replaced_block = os.linesep.join(replaced_blocks)
quote_children = text_to_children(replaced_block)
block_node = ParentNode("blockquote", children=quote_children)
children.append(block_node)
case BlockType.ORDERED_LIST:
ordered_list_children = []
ordered_list_items = block.splitlines()
for i in range(len(ordered_list_items)):
ordered_item = ordered_list_items[i]
ordered_item_children_nodes = text_to_children(ordered_item.lstrip(f"{i + 1}. "))
ordered_item_node = ParentNode(
"li", children=ordered_item_children_nodes
)
ordered_list_children.append(ordered_item_node)
ordered_list_node = ParentNode("ol", children=ordered_list_children)
children.append(ordered_list_node)
case BlockType.UNORDERED_LIST:
unordered_list_children = []
unordered_list_items = block.splitlines()
for unordered_item in unordered_list_items:
unordered_item_children_nodes = text_to_children(unordered_item.lstrip("- "))
unordered_item_node = ParentNode(
"li", children=unordered_item_children_nodes
)
unordered_list_children.append(unordered_item_node)
unordered_list_node = ParentNode("ul", children=unordered_list_children)
children.append(unordered_list_node)
case _:
raise NotImplemented("Unknown block type")
return ParentNode("div", children=children)
def text_to_children(markdown: str) -> list[HtmlNode]:
children = []
text_nodes = text_to_text_node(markdown)
for text_node in text_nodes:
html_node = text_node_to_html_node(text_node)
children.append(html_node)
return children
+36
View File
@@ -0,0 +1,36 @@
from pathlib import Path
def extract_title(markdown: str) -> str:
heading_identifier = "# "
lines = markdown.splitlines()
for line in lines:
if line.startswith(heading_identifier):
return line.lstrip(heading_identifier)
raise RuntimeError("No heading found that can be used as title")
def render_template(
template_path: str | Path,
title: str,
content: str,
basepath: str,
) -> str:
template = Path(template_path)
if not template.exists():
raise RuntimeError(f"Template file '{template}' does not exist")
if not template.is_file():
raise RuntimeError(f"Template file '{template}' is not a file")
template_content = template.read_text(encoding="utf-8")
rendered = template_content.replace("{{ Title }}", title)
rendered = rendered.replace("{{ Content }}", content)
rendered = rendered.replace('href="/', f'href="{basepath}')
rendered = rendered.replace('src="/', f'src="{basepath}')
return rendered
View File
@@ -1,6 +1,6 @@
import unittest
from block import BlockType, block_to_block_type
from src.markdown.blocks import BlockType, block_to_block_type
class TestBlock(unittest.TestCase):
@@ -1,6 +1,6 @@
import unittest
from extract import extract_markdown_images, extract_markdown_links
from src.markdown.extract import extract_markdown_images, extract_markdown_links
class TextExtract(unittest.TestCase):
@@ -1,6 +1,6 @@
import unittest
from htmlnode import HtmlNode
from src.nodes.html import HtmlNode
class TestHtmlNode(unittest.TestCase):
@@ -1,10 +1,13 @@
import os
import unittest
from splitnodes import (markdown_to_blocks, markdown_to_html_node,
split_nodes_delimiter, split_nodes_image,
split_nodes_link, text_to_text_node)
from textnode import TextNode, TextType
from src.markdown.inlines import (
split_nodes_delimiter,
split_nodes_image,
split_nodes_link,
text_to_text_node,
)
from src.markdown.to_html import markdown_to_blocks, markdown_to_html_node
from src.nodes.text import TextNode, TextType
class TestSplitNodes(unittest.TestCase):
@@ -198,9 +201,4 @@ the **same** even with inline stuff
node = markdown_to_html_node(md)
html = node.to_html()
with open("output.txt", "w") as file:
file.write(expected)
file.write("\n\n")
file.write(html)
self.assertEqual(html, expected)
@@ -1,6 +1,6 @@
import unittest
from leafnode import LeafNode
from src.nodes.leaf import LeafNode
class LeafNodeTest(unittest.TestCase):
@@ -1,9 +1,9 @@
import unittest
from typing import cast
from htmlnode import HtmlNode
from leafnode import LeafNode
from parentnode import ParentNode
from src.nodes.html import HtmlNode
from src.nodes.leaf import LeafNode
from src.nodes.parent import ParentNode
class TestParentNode(unittest.TestCase):
@@ -1,7 +1,7 @@
import unittest
from leafnode import LeafNode
from textnode import TextNode, TextType, text_node_to_html_node
from src.nodes.leaf import LeafNode
from src.nodes.text import TextNode, TextType, text_node_to_html_node
class TestTextNode(unittest.TestCase):
+1 -1
View File
@@ -1,2 +1,2 @@
#!/bin/bash
python3 -m unittest discover -s src
python3 -m unittest discover -s src/tests