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

1"""Data Transformation Density (DTD) for TypeScript.""" 

2 

3from __future__ import annotations 

4 

5_TS_DTD_CONTAINERS = {"object", "array"} 

6 

7_TS_DTD_PAYLOADS = {"pair", "shorthand_property_identifier"} 

8 

9_TS_DTD_MAP_NAMES = {"map", "filter", "reduce", "flatMap", "forEach"} 

10 

11_TS_DTD_PUNCTUATION = {"[", "]", "{", "}", ",", "comment"} 

12 

13 

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 

24 

25 

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) 

31 

32 

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 

45 

46 

47_TS_DTD_NESTED_FUNCS = {"function_declaration", "function_expression", 

48 "method_definition", "generator_function_declaration"} 

49 

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 

57 

58 

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 

68 

69 

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 

77 

78 

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 

88 

89 is_element = is_array and child.type not in _TS_DTD_PUNCTUATION 

90 if is_element: 

91 total += depth 

92 

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) 

101 

102 return total