phml.misc.component

 1from pathlib import Path
 2
 3from phml.nodes import AST, All_Nodes, Element
 4
 5__all__ = [
 6    "tag_from_file",
 7    "filename_from_path",
 8    "parse_component",
 9    "valid_component_dict",
10]
11
12
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
21
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.")
27
28    tokens = []
29    for token in finditer(r"(\b|[A-Z]|_|-)([a-z]+)|([A-Z]+)(?=[^a-z])", filename):
30        first, rest, cap = token.groups()
31
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())
37
38    return "-".join(tokens)
39
40
41def filename_from_path(file: Path) -> str:
42    """Get the filename without the suffix from a pathlib.Path."""
43
44    if file.is_file():
45        return file.name.replace(file.suffix, "")
46
47    raise TypeError("Path must also be a valid file.")
48
49
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    )
58
59
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    )
68
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                )
87
88    if result["component"] is None:
89        raise Exception("Must have at least one element in a component.")
90
91    return result
def tag_from_file(filename: str | pathlib.Path) -> str:
14def tag_from_file(filename: str | Path) -> str:
15    """Generates a tag name some-tag-name from a filename.
16    Assumes filenames of:
17    * snakecase - some_file_name
18    * camel case - someFileName
19    * pascal case - SomeFileName
20    """
21    from re import finditer  # pylint: disable=import-outside-toplevel
22
23    if isinstance(filename, Path):
24        if filename.is_file():
25            filename = filename.name.replace(filename.suffix, "")
26        else:
27            raise TypeError("If filename is a path it must also be a valid file.")
28
29    tokens = []
30    for token in finditer(r"(\b|[A-Z]|_|-)([a-z]+)|([A-Z]+)(?=[^a-z])", filename):
31        first, rest, cap = token.groups()
32
33        if first is not None and first.isupper():
34            rest = first + rest
35        elif cap is not None and cap.isupper():
36            rest = cap
37        tokens.append(rest.lower())
38
39    return "-".join(tokens)

Generates a tag name some-tag-name from a filename. Assumes filenames of:

  • snakecase - some_file_name
  • camel case - someFileName
  • pascal case - SomeFileName
def filename_from_path(file: pathlib.Path) -> str:
42def filename_from_path(file: Path) -> str:
43    """Get the filename without the suffix from a pathlib.Path."""
44
45    if file.is_file():
46        return file.name.replace(file.suffix, "")
47
48    raise TypeError("Path must also be a valid file.")

Get the filename without the suffix from a pathlib.Path.

def parse_component(ast: phml.nodes.AST.AST) -> dict[str, phml.nodes.element.Element]:
61def parse_component(ast: AST) -> dict[str, Element]:
62    """Helper function to parse the components elements."""
63    from phml import (  # pylint: disable=import-outside-toplevel
64        check,
65        is_css_style,
66        is_javascript,
67        visit_children,
68    )
69
70    result = {"python": [], "script": [], "style": [], "component": None}
71    for node in visit_children(ast.tree):
72        if check(node, ["element", {"tag": "python"}]):
73            result["python"].append(node)
74        elif check(node, ["element", {"tag": "script"}]) and is_javascript(node):
75            result["script"].append(node)
76        elif check(node, ["element", {"tag": "style"}]) and is_css_style(node):
77            result["style"].append(node)
78        elif check(node, "element"):
79            if result["component"] is None:
80                result["component"] = node
81            else:
82                raise Exception(
83                    """\
84Components may only have one wrapping element. All other element in the root must be either a \
85script, style, or python tag.\
86"""
87                )
88
89    if result["component"] is None:
90        raise Exception("Must have at least one element in a component.")
91
92    return result

Helper function to parse the components elements.

def valid_component_dict(cmpt: dict) -> bool:
51def valid_component_dict(cmpt: dict) -> bool:
52    """Check if a component dict is valid."""
53    return bool(
54        ("python" in cmpt and isinstance(cmpt["python"], list))
55        and ("script" in cmpt and isinstance(cmpt["script"], list))
56        and ("style" in cmpt and isinstance(cmpt["script"], list))
57        and ("component" in cmpt and isinstance(cmpt["component"], All_Nodes))
58    )

Check if a component dict is valid.