@@ -85,15 +85,86 @@ and output_dot graph out_channel =
8585(* Output as JSON format *)
8686and output_json graph out_channel =
8787 let modules = Dependency_graph. get_modules graph in
88+
89+ (* Pre-compute all expensive operations once *)
8890 let metrics = Dependency_graph. calculate_metrics graph in
8991 let cycles = Dependency_graph. find_all_cycles graph in
92+ let sccs = Dependency_graph. find_strongly_connected_components graph in
93+
94+ (* Create a lookup table for modules in cycles *)
95+ let in_cycle_table = Hashtbl. create (List. length modules) in
96+ List. iter
97+ (fun scc ->
98+ if List. length scc > 1 then
99+ List. iter (fun m -> Hashtbl. replace in_cycle_table m true ) scc)
100+ sccs;
101+
102+ (* Pre-compute all module paths and create a lookup map *)
103+ let path_map = Hashtbl. create (List. length modules) in
104+ List. iter
105+ (fun m ->
106+ match Dependency_graph. get_module_path graph m with
107+ | Some path -> Hashtbl. add path_map m path
108+ | None -> () )
109+ modules;
90110
91111 (* Get all source directories from module paths for dependency resolution *)
92- let source_dirs =
93- modules
94- |> List. filter_map (fun m -> Dependency_graph. get_module_path graph m)
95- |> List. map Filename. dirname
96- |> List. sort_uniq String. compare
112+ let source_dirs = ref [] in
113+ List. iter
114+ (fun m ->
115+ match Dependency_graph. get_module_path graph m with
116+ | Some path ->
117+ let dir = Filename. dirname path in
118+ if not (List. mem dir ! source_dirs) then
119+ source_dirs := dir :: ! source_dirs
120+ | None -> () )
121+ modules;
122+
123+ (* Pre-compute all dependencies and dependents *)
124+ let deps_map = Hashtbl. create (List. length modules) in
125+ let dependents_map = Hashtbl. create (List. length modules) in
126+
127+ (* First compute all dependencies *)
128+ List. iter
129+ (fun m ->
130+ let deps = Dependency_graph. get_dependencies graph m in
131+ Hashtbl. add deps_map m deps)
132+ modules;
133+
134+ (* Then compute all dependents using the deps map *)
135+ List. iter
136+ (fun m ->
137+ let deps = try Hashtbl. find deps_map m with Not_found -> [] in
138+ List. iter
139+ (fun dep ->
140+ let current_dependents =
141+ try Hashtbl. find dependents_map dep with Not_found -> []
142+ in
143+ Hashtbl. replace dependents_map dep (m :: current_dependents))
144+ deps)
145+ modules;
146+
147+ (* Helper function to resolve path for a module *)
148+ let resolve_path m =
149+ try Some (Hashtbl. find path_map m)
150+ with Not_found -> (
151+ match Parse_utils. find_implementation_file_by_name m ! source_dirs with
152+ | Some p ->
153+ Hashtbl. add path_map m p;
154+ Some p
155+ | None -> (
156+ (* If not found, try to resolve from node_modules *)
157+ let node_path_opt =
158+ if List. length ! source_dirs > 0 then
159+ Parse_utils. find_external_module_path m
160+ (Filename. dirname (List. hd ! source_dirs))
161+ else None
162+ in
163+ match node_path_opt with
164+ | Some p ->
165+ Hashtbl. add path_map m p;
166+ Some p
167+ | None -> None ))
97168 in
98169
99170 (* JSON opening *)
@@ -104,15 +175,19 @@ and output_json graph out_channel =
104175
105176 List. iteri
106177 (fun i module_name ->
107- let deps = Dependency_graph. get_dependencies graph module_name in
108- let dependents = Dependency_graph. find_dependents graph module_name in
109- let path = Dependency_graph. get_module_path graph module_name in
178+ let deps = try Hashtbl. find deps_map module_name with Not_found -> [] in
179+ let dependents =
180+ try Hashtbl. find dependents_map module_name with Not_found -> []
181+ in
182+ let path_opt =
183+ try Some (Hashtbl. find path_map module_name) with Not_found -> None
184+ in
110185
111186 output_string out_channel " {\n " ;
112187 output_string out_channel (" \" name\" : \" " ^ module_name ^ " \" ,\n " );
113188
114189 (* Add file path info if available *)
115- (match path with
190+ (match path_opt with
116191 | Some path_str ->
117192 output_string out_channel (" \" path\" : \" " ^ path_str ^ " \" ,\n " )
118193 | None -> output_string out_channel " \" path\" : null,\n " );
@@ -124,27 +199,13 @@ and output_json graph out_channel =
124199 output_string out_channel " \n " ;
125200 List. iteri
126201 (fun j dep ->
127- let dep_path = Dependency_graph. get_module_path graph dep in
202+ let dep_path_opt = resolve_path dep in
203+
128204 output_string out_channel " {\n " ;
129205 output_string out_channel (" \" name\" : \" " ^ dep ^ " \" " );
130206
131- (* Try to resolve path if it's null *)
132- let resolved_path =
133- match dep_path with
134- | Some path_str -> Some path_str
135- | None -> (
136- match
137- Parse_utils. find_implementation_file_by_name dep source_dirs
138- with
139- | Some path -> Some path
140- | None ->
141- (* If not found, try to resolve from node_modules *)
142- Parse_utils. find_external_module_path dep
143- (Filename. dirname (List. hd source_dirs)))
144- in
145-
146207 (* Add dependency file path if available *)
147- (match resolved_path with
208+ (match dep_path_opt with
148209 | Some path_str ->
149210 output_string out_channel
150211 (" ,\n \" path\" : \" " ^ path_str ^ " \" " )
@@ -165,30 +226,17 @@ and output_json graph out_channel =
165226 output_string out_channel " \n " ;
166227 List. iteri
167228 (fun j dependent ->
168- let dependent_path =
169- match Dependency_graph. get_module_path graph dependent with
170- | Some path -> Some path
171- | None -> (
172- match
173- Parse_utils. find_implementation_file_by_name dependent
174- source_dirs
175- with
176- | Some path -> Some path
177- | None ->
178- (* If not found, try to resolve from node_modules *)
179- Parse_utils. find_external_module_path dependent
180- (Filename. dirname (List. hd source_dirs)))
181- in
229+ let dependent_path_opt = resolve_path dependent in
182230
183231 output_string out_channel " {\n " ;
184232 output_string out_channel
185233 (" \" name\" : \" " ^ dependent ^ " \" " );
186234
187235 (* Add dependent file path if available *)
188- (match dependent_path with
189- | Some path ->
236+ (match dependent_path_opt with
237+ | Some path_str ->
190238 output_string out_channel
191- (" ,\n \" path\" : \" " ^ path ^ " \" " )
239+ (" ,\n \" path\" : \" " ^ path_str ^ " \" " )
192240 | None -> output_string out_channel " ,\n \" path\" : null" );
193241
194242 output_string out_channel " \n }" ;
@@ -207,11 +255,9 @@ and output_json graph out_channel =
207255 output_string out_channel
208256 (" ,\n \" fan_out\" : " ^ string_of_int fan_out);
209257
210- (* Check for circular dependencies *)
258+ (* Check for circular dependencies using pre-computed table *)
211259 let is_in_cycle =
212- List. exists
213- (fun scc -> List. length scc > 1 && List. mem module_name scc)
214- (Dependency_graph. find_strongly_connected_components graph)
260+ try Hashtbl. find in_cycle_table module_name with Not_found -> false
215261 in
216262 output_string out_channel
217263 (" ,\n \" in_cycle\" : " ^ string_of_bool is_in_cycle);
@@ -266,8 +312,10 @@ and output_json graph out_channel =
266312
267313 let format_float f =
268314 let s = string_of_float f in
269- if String. ends_with ~suffix: " ." s then s ^ " 0"
270- (* Add 0 if ending with decimal point *) else s
315+ let len = String. length s in
316+ if len > 0 && s.[len - 1 ] = '.' then s ^ " 0"
317+ (* Add 0 if ending with decimal point *)
318+ else s
271319 in
272320
273321 output_string out_channel
0 commit comments