Coverage for phml\misc\component.py: 100%
41 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-12-08 16:33 -0600
« prev ^ index » next coverage.py v6.5.0, created at 2022-12-08 16:33 -0600
1from pathlib import Path
3from phml.nodes import AST, All_Nodes, Element
5__all__ = [
6 "tag_from_file",
7 "filename_from_path",
8 "parse_component",
9 "valid_component_dict",
10]
13def tag_from_file(filename: str | Path) -> str:
14 """Generates a tag name some-tag-name from a filename.
15 Assumes filenames of:
16 * snakecase - some_file_name
17 * camel case - someFileName
18 * pascal case - SomeFileName
19 """
20 from re import finditer # pylint: disable=import-outside-toplevel
22 if isinstance(filename, Path):
23 if filename.is_file():
24 filename = filename.name.replace(filename.suffix, "")
25 else:
26 raise TypeError("If filename is a path it must also be a valid file.")
28 tokens = []
29 for token in finditer(r"(\b|[A-Z]|_|-)([a-z]+)|([A-Z]+)(?=[^a-z])", filename):
30 first, rest, cap = token.groups()
32 if first is not None and first.isupper():
33 rest = first + rest
34 elif cap is not None and cap.isupper():
35 rest = cap
36 tokens.append(rest.lower())
38 return "-".join(tokens)
41def filename_from_path(file: Path) -> str:
42 """Get the filename without the suffix from a pathlib.Path."""
44 if file.is_file():
45 return file.name.replace(file.suffix, "")
47 raise TypeError("Path must also be a valid file.")
50def valid_component_dict(cmpt: dict) -> bool:
51 """Check if a component dict is valid."""
52 return bool(
53 ("python" in cmpt and isinstance(cmpt["python"], list))
54 and ("script" in cmpt and isinstance(cmpt["script"], list))
55 and ("style" in cmpt and isinstance(cmpt["script"], list))
56 and ("component" in cmpt and isinstance(cmpt["component"], All_Nodes))
57 )
60def parse_component(ast: AST) -> dict[str, Element]:
61 """Helper function to parse the components elements."""
62 from phml import ( # pylint: disable=import-outside-toplevel
63 check,
64 is_css_style,
65 is_javascript,
66 visit_children,
67 )
69 result = {"python": [], "script": [], "style": [], "component": None}
70 for node in visit_children(ast.tree):
71 if check(node, ["element", {"tag": "python"}]):
72 result["python"].append(node)
73 elif check(node, ["element", {"tag": "script"}]) and is_javascript(node):
74 result["script"].append(node)
75 elif check(node, ["element", {"tag": "style"}]) and is_css_style(node):
76 result["style"].append(node)
77 elif check(node, "element"):
78 if result["component"] is None:
79 result["component"] = node
80 else:
81 raise Exception(
82 """\
83Components may only have one wrapping element. All other element in the root must be either a \
84script, style, or python tag.\
85"""
86 )
88 if result["component"] is None:
89 raise Exception("Must have at least one element in a component.")
91 return result