Coverage for metrics / dtd / typescript.py: 93%
69 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 15:04 -0800
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 15:04 -0800
1"""Data Transformation Density (DTD) for TypeScript."""
3from __future__ import annotations
5_TS_DTD_CONTAINERS = {"object", "array"}
7_TS_DTD_PAYLOADS = {"pair", "shorthand_property_identifier"}
9_TS_DTD_MAP_NAMES = {"map", "filter", "reduce", "flatMap", "forEach"}
11_TS_DTD_PUNCTUATION = {"[", "]", "{", "}", ",", "comment"}
14def compute_dtd_ts(func_node) -> int:
15 """Compute Data Transformation Density for a TypeScript function node."""
16 for child in func_node.children:
17 if child.type == "statement_block":
18 return _dtd_walk_ts(child, depth=0, top_func=func_node)
19 if func_node.type == "arrow_function":
20 children = [c for c in func_node.children if c.type not in ("=>", "formal_parameters", "identifier")]
21 for child in children:
22 return _dtd_walk_ts(child, depth=0, top_func=func_node)
23 return 0
26def _is_template_container(node) -> bool:
27 """Check if a template_string has template_substitution children (making it a container)."""
28 if node.type != "template_string":
29 return False
30 return any(c.type == "template_substitution" for c in node.children)
33def _is_map_call_ts(node) -> bool:
34 """Check if a call_expression is a map/filter/reduce/flatMap/forEach call."""
35 if node.type != "call_expression":
36 return False
37 if not node.children:
38 return False
39 first = node.children[0]
40 if first.type == "member_expression":
41 prop = first.child_by_field_name("property")
42 if prop and prop.text.decode() in _TS_DTD_MAP_NAMES:
43 return True
44 return False
47_TS_DTD_NESTED_FUNCS = {"function_declaration", "function_expression",
48 "method_definition", "generator_function_declaration"}
50def _dtd_is_nested_func(child, top_func) -> bool:
51 """Check if a child is a nested function that should be skipped or handled specially."""
52 if child.type in _TS_DTD_NESTED_FUNCS and child is not top_func:
53 return True
54 if child.type == "arrow_function" and child is not top_func:
55 return True
56 return False
59def _dtd_increases_depth(child) -> bool:
60 """Check if a child node should recurse with depth + 1."""
61 if child.type in _TS_DTD_CONTAINERS:
62 return True
63 if _is_template_container(child):
64 return True
65 if _is_map_call_ts(child):
66 return True
67 return False
70def _dtd_is_payload(child, node) -> bool:
71 """Check if a child is a payload node that scores at current depth."""
72 if child.type in _TS_DTD_PAYLOADS:
73 return True
74 if child.type == "template_substitution" and node.type == "template_string":
75 return True
76 return False
79def _dtd_walk_ts(node, depth: int, top_func) -> int:
80 """Walk AST and accumulate DTD score."""
81 total = 0
82 is_array = node.type == "array"
83 for child in node.children:
84 if _dtd_is_nested_func(child, top_func):
85 if child.type == "arrow_function" and not (child.parent and child.parent.type == "variable_declarator"):
86 total += _dtd_walk_ts(child, depth, top_func)
87 continue
89 is_element = is_array and child.type not in _TS_DTD_PUNCTUATION
90 if is_element:
91 total += depth
93 if _dtd_increases_depth(child):
94 total += _dtd_walk_ts(child, depth + 1, top_func)
95 elif _dtd_is_payload(child, node):
96 if not is_element:
97 total += depth
98 total += _dtd_walk_ts(child, depth, top_func)
99 else:
100 total += _dtd_walk_ts(child, depth, top_func)
102 return total