@@ -32,19 +32,10 @@ const noRepeatedMemberAccess = createRule({
32
32
private modified : boolean = false ;
33
33
private parent ?: ChainNode ;
34
34
private children : Map < string , ChainNode > = new Map ( ) ;
35
- private path : string ;
36
35
37
- constructor ( name : string , parent ?: ChainNode ) {
36
+ constructor ( parent ?: ChainNode ) {
38
37
this . parent = parent ;
39
38
this . modified = this . parent ?. modified || false ;
40
-
41
- if ( name === "__root__" ) {
42
- this . path = "" ;
43
- } else if ( ! this . parent || this . parent . path === "" ) {
44
- this . path = name ;
45
- } else {
46
- this . path = this . parent . path + "." + name ;
47
- }
48
39
}
49
40
50
41
get getCount ( ) : number {
@@ -55,26 +46,18 @@ const noRepeatedMemberAccess = createRule({
55
46
return this . modified ;
56
47
}
57
48
58
- get getParent ( ) : ChainNode | undefined {
59
- return this . parent ;
60
- }
61
-
62
49
get getChildren ( ) : Map < string , ChainNode > {
63
50
return this . children ;
64
51
}
65
52
66
- get getPath ( ) : string {
67
- return this . path ;
68
- }
69
-
70
53
incrementCount ( ) : void {
71
54
this . count ++ ;
72
55
}
73
56
74
57
// Get or create child node
75
58
getOrCreateChild ( childName : string ) : ChainNode {
76
59
if ( ! this . children . has ( childName ) ) {
77
- this . children . set ( childName , new ChainNode ( childName , this ) ) ;
60
+ this . children . set ( childName , new ChainNode ( this ) ) ;
78
61
}
79
62
return this . children . get ( childName ) ! ;
80
63
}
@@ -90,47 +73,49 @@ const noRepeatedMemberAccess = createRule({
90
73
91
74
// Root node for the tree (per scope)
92
75
class ChainTree {
93
- private root : ChainNode = new ChainNode ( "__root__" ) ;
76
+ private root : ChainNode = new ChainNode ( ) ;
94
77
95
- // Insert a chain path into the tree and increment counts
96
- insertChain ( properties : string [ ] ) : void {
78
+ // Visitor function to navigate through property chain
79
+ private visitChainPath (
80
+ properties : string [ ] ,
81
+ process : ( node : ChainNode ) => void
82
+ ) : ChainNode {
97
83
let current = this . root ;
98
84
99
- // Navigate/create path in tree
85
+ // Navigate/process node in the tree
100
86
for ( const prop of properties ) {
101
87
const child = current . getOrCreateChild ( prop ) ;
102
88
current = child ;
103
-
104
- // Only increment count for non-single properties (chains with dots)
105
- if ( properties . length > 1 ) {
106
- current . incrementCount ( ) ;
107
- }
89
+ process ( current ) ;
108
90
}
91
+
92
+ return current ;
93
+ }
94
+
95
+ // Insert a chain path into the tree and increment counts
96
+ insertChain ( properties : string [ ] ) : void {
97
+ this . visitChainPath ( properties , ( node ) => {
98
+ node . incrementCount ( ) ;
99
+ } ) ;
109
100
}
110
101
111
102
// Mark a chain and its descendants as modified
112
103
markChainAsModified ( properties : string [ ] ) : void {
113
- let current = this . root ;
114
-
115
- // Navigate to the target node, creating nodes if they don't exist
116
- for ( const prop of properties ) {
117
- const newChild = current . getOrCreateChild ( prop ) ;
118
- current = newChild ;
119
- }
104
+ const targetNode = this . visitChainPath ( properties , ( ) => { } ) ;
120
105
121
106
// Mark this node and all descendants as modified
122
- current . markAsModified ( ) ;
107
+ targetNode . markAsModified ( ) ;
123
108
}
124
109
125
110
// Find any valid chain that meets the minimum occurrence threshold
126
111
findValidChains ( ) {
127
112
const validChains : Array < { chain : string } > = [ ] ;
128
113
129
- const traverse = ( node : ChainNode , depth : number ) => {
114
+ const dfs = ( node : ChainNode , pathArray : string [ ] ) => {
130
115
// Only consider chains with more than one segment (has dots)
131
- if ( depth > 1 && ! node . isModified && node . getCount >= 2 ) {
116
+ if ( pathArray . length > 1 && ! node . isModified && node . getCount >= 2 ) {
132
117
validChains . push ( {
133
- chain : node . getPath ,
118
+ chain : pathArray . join ( "." ) ,
134
119
} ) ;
135
120
}
136
121
@@ -140,12 +125,15 @@ const noRepeatedMemberAccess = createRule({
140
125
}
141
126
142
127
// Recursively traverse children
143
- for ( const child of node . getChildren . values ( ) ) {
144
- traverse ( child , depth + 1 ) ;
128
+ for ( const [ childName , child ] of node . getChildren ) {
129
+ pathArray . push ( childName ) ;
130
+ dfs ( child , pathArray ) ;
131
+ pathArray . pop ( ) ;
145
132
}
146
133
} ;
147
134
148
- traverse ( this . root , 0 ) ;
135
+ // Start DFS from root with empty path array
136
+ dfs ( this . root , [ ] ) ;
149
137
return validChains ;
150
138
}
151
139
}
0 commit comments