@@ -220,21 +220,23 @@ let string_of_module_id
220220 match Config_util. find_opt cmj_file with
221221 | Some cmj_path ->
222222 let cmj_dir = Filename. dirname cmj_path in
223- let lib_bs_pattern = " / lib/bs/ " in
223+ (* Platform-independent: look for " lib<sep>bs<sep>" where <sep> is / or \\ *)
224224 let source_dir =
225225 try
226- (* Find "/lib/bs/" in the path and extract everything after it *)
227- let idx = String. rindex_from cmj_dir (String. length cmj_dir - 1 ) '/' in
226+ let sep = Filename. dir_sep.[0 ] in
227+ let lib_bs = " lib" ^ Filename. dir_sep ^ " bs" ^ Filename. dir_sep in
228+ (* Find "lib/bs/" or "lib\\bs\\" in the path and extract everything after it *)
229+ let idx = String. rindex_from cmj_dir (String. length cmj_dir - 1 ) sep in
228230 let rec find_lib_bs pos =
229231 if pos < 0 then None
230- else if Ext_string. starts_with (String. sub cmj_dir pos (String. length cmj_dir - pos)) lib_bs_pattern then
231- Some (pos + String. length lib_bs_pattern )
232+ else if Ext_string. starts_with (String. sub cmj_dir pos (String. length cmj_dir - pos)) lib_bs then
233+ Some (pos + String. length lib_bs )
232234 else
233235 find_lib_bs (pos - 1 )
234236 in
235237 match find_lib_bs idx with
236238 | Some start_idx ->
237- (* Example: extract "src/core/intl" from ".../lib/bs/src/core/intl" *)
239+ (* Example: extract "src/core/intl" from ".../lib/bs/src/core/intl" or "...\\lib\\bs\\src\\core\\intl" *)
238240 String. sub cmj_dir start_idx (String. length cmj_dir - start_idx)
239241 | None -> cmj_dir
240242 with Not_found -> cmj_dir
@@ -272,26 +274,36 @@ let string_of_module_id
272274 We extract the actual source directory from the dependency's .cmj file
273275 location and reconstruct the import path correctly.
274276 *)
275- (* External package imports: check if pkg_rel_path ends with "/."
277+ (* External package imports: check if pkg_rel_path ends with "/." or "\."
276278 which indicates the dependency uses in-source builds *)
277- if Ext_string. ends_with dep_pkg.pkg_rel_path " /." then begin
279+ let ends_with_dot =
280+ Ext_string. ends_with dep_pkg.pkg_rel_path " /." ||
281+ Ext_string. ends_with dep_pkg.pkg_rel_path " \\ ."
282+ in
283+ if ends_with_dot then begin
278284 let cmj_file = dep_module_id.id.name ^ Literals. suffix_cmj in
279285 (* Prefer lib/bs over lib/ocaml as lib/bs preserves source directory structure *)
286+ let lib_bs_pattern = " lib" ^ Filename. dir_sep ^ " bs" ^ Filename. dir_sep in
287+ let lib_ocaml_pattern = " lib" ^ Filename. dir_sep ^ " ocaml" ^ Filename. dir_sep in
280288 let cmj_opt =
281289 match Config_util. find_opt cmj_file with
282- | Some path when Ext_string. contain_substring path " /lib/bs/ " ->
290+ | Some path when Ext_string. contain_substring path lib_bs_pattern ->
283291 Some path
284292 | Some ocaml_path ->
285293 (* Found lib/ocaml, derive lib/bs path from it *)
286- let lib_ocaml_pattern = " /lib/ocaml/" in
287294 let pkg_root =
288295 try
296+ let sep = Filename. dir_sep.[0 ] in
289297 let rec find_lib_ocaml pos =
290298 if pos < 0 then None
291299 else if Ext_string. starts_with (String. sub ocaml_path pos (String. length ocaml_path - pos)) lib_ocaml_pattern then
292300 Some (String. sub ocaml_path 0 pos)
293301 else
294- find_lib_ocaml (pos - 1 )
302+ let next_pos =
303+ try String. rindex_from ocaml_path (pos - 1 ) sep
304+ with Not_found -> - 1
305+ in
306+ find_lib_ocaml next_pos
295307 in
296308 find_lib_ocaml (String. length ocaml_path - 1 )
297309 with Not_found -> None
@@ -326,37 +338,121 @@ let string_of_module_id
326338 | None -> Some ocaml_path)
327339 | None -> None
328340 in
329- match cmj_opt with
330- | Some cmj_path ->
331- (* External packages store .cmj at node_modules/<pkg>/lib/bs/<source_dir>/<module>.cmj
332- Example: /Users/barry/Projects/rescript/node_modules/a/lib/bs/src/A-A.cmj
333- We extract "src" from this path. *)
334- let cmj_dir = Filename. dirname cmj_path in
335- let lib_bs_pattern = " /lib/bs/" in
336- let source_dir =
337- try
338- let idx = String. rindex_from cmj_dir (String. length cmj_dir - 1 ) '/' in
339- let rec find_lib_bs pos =
340- if pos < 0 then None
341- else if Ext_string. starts_with (String. sub cmj_dir pos (String. length cmj_dir - pos)) lib_bs_pattern then
342- Some (pos + String. length lib_bs_pattern)
343- else
344- find_lib_bs (pos - 1 )
345- in
346- match find_lib_bs idx with
347- | Some start_idx ->
348- String. sub cmj_dir start_idx (String. length cmj_dir - start_idx)
349- | None -> " ."
350- with Not_found -> " ."
351- in
352- (* Extract package name from pkg_rel_path: "a/." -> "a" *)
341+ match cmj_opt with
342+ | Some cmj_path ->
343+ (* External packages store .cmj at node_modules/<pkg>/lib/bs/<source_dir>/<module>.cmj
344+ Example: /Users/barry/Projects/rescript/node_modules/a/lib/bs/src/A-A.cmj
345+ Or on Windows: C:\Users\barry\node_modules\a\lib\bs\src\A-A.cmj
346+ We extract "src" from this path.
347+
348+ For namespaced packages, there may be a namespace file at the root (lib/bs/A.cmj)
349+ and the actual module in a subdirectory (lib/bs/src/A-A.cmj). We want the latter. *)
350+ let cmj_dir = Filename. dirname cmj_path in
351+ let sep = Filename. dir_sep.[0 ] in
352+ let lib_bs_pattern = " lib" ^ Filename. dir_sep ^ " bs" ^ Filename. dir_sep in
353+ let source_dir =
354+ try
355+ let idx = String. rindex_from cmj_dir (String. length cmj_dir - 1 ) sep in
356+ let rec find_lib_bs pos =
357+ if pos < 0 then None
358+ else if Ext_string. starts_with (String. sub cmj_dir pos (String. length cmj_dir - pos)) lib_bs_pattern then
359+ Some (pos + String. length lib_bs_pattern)
360+ else
361+ let next_pos =
362+ try String. rindex_from cmj_dir (pos - 1 ) sep
363+ with Not_found -> - 1
364+ in
365+ find_lib_bs next_pos
366+ in
367+ match find_lib_bs idx with
368+ | Some start_idx ->
369+ String. sub cmj_dir start_idx (String. length cmj_dir - start_idx)
370+ | None -> " ."
371+ with Not_found -> " ."
372+ in
373+ (* Extract package name from pkg_rel_path: "a/." or "a\\." -> "a" *)
353374 let pkg_name =
354375 String. sub dep_pkg.pkg_rel_path 0 (String. length dep_pkg.pkg_rel_path - 2 )
355376 in
356- if source_dir = " ." then begin
377+ (* If source_dir is ".", we found a namespace file at the root.
378+ Try to find the actual module in subdirectories by searching lib/bs recursively. *)
379+ let final_source_dir =
380+ if source_dir = " ." then begin
381+ (* Derive package root from cmj_path: .../node_modules/a/lib/bs/A.cmj -> .../node_modules/a *)
382+ try
383+ let rec find_pkg_root path =
384+ let parent = Filename. dirname path in
385+ let basename = Filename. basename path in
386+ if basename = pkg_name then Some path (* Return the path itself, not parent *)
387+ else if parent = path then None
388+ else find_pkg_root parent
389+ in
390+ match find_pkg_root cmj_path with
391+ | Some pkg_root ->
392+ let (// ) = Filename. concat in
393+ let lib_bs_dir = pkg_root // " lib" // " bs" in
394+ (* Recursively search for the module file in subdirectories.
395+ For namespaced modules, the file may be A-Namespace.cmj instead of A.cmj *)
396+ let rec find_in_dir dir =
397+ (* Use the original module name directly, don't try to extract from js_file *)
398+ let module_base = dep_module_id.id.name in
399+ (* Check both exact match (A.cmj) and namespace pattern (A-*.cmj) *)
400+ let cmj_exact = module_base ^ Literals. suffix_cmj in
401+ let cmj_pattern_prefix = module_base ^ " -" in
402+
403+ (* First check if dir itself contains a matching file *)
404+ let found_in_current_dir =
405+ if dir <> lib_bs_dir then begin
406+ try
407+ let files = Sys. readdir dir in
408+ Array. fold_left (fun acc file ->
409+ match acc with
410+ | Some _ -> acc
411+ | None ->
412+ if file = cmj_exact || Ext_string. starts_with file cmj_pattern_prefix then begin
413+ if Ext_string. ends_with file Literals. suffix_cmj then begin
414+ let full_path = dir // file in
415+ if Sys. file_exists full_path && not (Sys. is_directory full_path) then
416+ (* Found in a subdirectory, extract relative path from lib/bs/ *)
417+ let rel_from_lib_bs = String. sub dir (String. length lib_bs_dir + 1 ) (String. length dir - String. length lib_bs_dir - 1 ) in
418+ Some rel_from_lib_bs
419+ else None
420+ end else None
421+ end else None
422+ ) None files
423+ with _ -> None
424+ end else None
425+ in
426+
427+ match found_in_current_dir with
428+ | Some _ -> found_in_current_dir
429+ | None ->
430+ (* Not found in current dir, search subdirectories *)
431+ try
432+ let subdirs = Sys. readdir dir in
433+ Array. fold_left (fun acc subdir ->
434+ match acc with
435+ | Some _ -> acc
436+ | None ->
437+ let sub_path = dir // subdir in
438+ if Sys. is_directory sub_path then find_in_dir sub_path
439+ else None
440+ ) None subdirs
441+ with _ -> None
442+ in
443+ (match find_in_dir lib_bs_dir with
444+ | Some subdir -> subdir
445+ | None -> " ." )
446+ | None -> " ."
447+ with _ -> " ."
448+ end else
449+ source_dir
450+ in
451+ if final_source_dir = " ." then
452+ (* Still couldn't find it, use default *)
357453 dep_pkg.pkg_rel_path // js_file
358- end else begin
359- let result = pkg_name // source_dir // js_file in
454+ else begin
455+ let result = pkg_name // final_source_dir // js_file in
360456 (* Reconstruct: "a" + "src" + "A.res.js" = "a/src/A.res.js" *)
361457 result
362458 end
0 commit comments