@@ -129,7 +129,7 @@ let string_of_module_id
129129
130130 let dep_info_query =
131131 Js_packages_info. query_package_infos dep_package_info module_system
132- in
132+ in
133133 match dep_info_query, current_info_query with
134134 | Package_not_found , _ ->
135135 Bs_exception. error (Missing_ml_dependency dep_module_id.id.name)
@@ -138,10 +138,51 @@ let string_of_module_id
138138 | (Package_script | Package_found _ ), Package_not_found -> assert false
139139
140140 | Package_found ({suffix} as pkg), Package_script
141- ->
141+ ->
142142 let js_file =
143- Ext_namespace. js_name_of_modulename dep_module_id.id.name case suffix in
144- pkg.pkg_rel_path // js_file
143+ Ext_namespace. js_name_of_modulename dep_module_id.id.name case suffix in
144+ (* External package imports: check if pkg_rel_path ends with "/."
145+ which indicates the dependency uses in-source builds *)
146+ if Ext_string. ends_with pkg.pkg_rel_path " /." then begin
147+ let cmj_file = dep_module_id.id.name ^ Literals. suffix_cmj in
148+ match Config_util. find_opt cmj_file with
149+ | Some cmj_path ->
150+ (* External packages store .cmj at node_modules/<pkg>/lib/bs/<source_dir>/<module>.cmj
151+ Example: /Users/barry/Projects/great-project/node_modules/a/lib/bs/src/A-A.cmj
152+ We extract "src" from this path. *)
153+ let cmj_dir = Filename. dirname cmj_path in
154+ let lib_bs_pattern = " /lib/bs/" in
155+ let source_dir =
156+ try
157+ let rec find_lib_bs pos =
158+ if pos < 0 then None
159+ else if Ext_string. starts_with (String. sub cmj_dir pos (String. length cmj_dir - pos)) lib_bs_pattern then
160+ Some (pos + String. length lib_bs_pattern)
161+ else
162+ find_lib_bs (pos - 1 )
163+ in
164+ match find_lib_bs (String. length cmj_dir - 1 ) with
165+ | Some start_idx ->
166+ String. sub cmj_dir start_idx (String. length cmj_dir - start_idx)
167+ | None -> " ."
168+ with Not_found -> " ."
169+ in
170+ (* Extract package name from pkg_rel_path: "a/." -> "a" *)
171+ let pkg_name =
172+ String. sub pkg.pkg_rel_path 0 (String. length pkg.pkg_rel_path - 2 )
173+ in
174+ if source_dir = " ." then begin
175+ pkg.pkg_rel_path // js_file
176+ end else begin
177+ let result = pkg_name // source_dir // js_file in
178+ (* Reconstruct: "a" + "src" + "A.res.js" = "a/src/A.res.js" *)
179+ result
180+ end
181+ | None ->
182+ pkg.pkg_rel_path // js_file
183+ end else begin
184+ pkg.pkg_rel_path // js_file
185+ end
145186 | Package_found ({suffix } as dep_pkg),
146187 Package_found cur_pkg ->
147188 let js_file =
@@ -216,62 +257,113 @@ let string_of_module_id
216257 (* TODO: we assume that both [x] and [path] could only be relative path
217258 which is guaranteed by [-bs-package-output]
218259 *)
219- else
220- if Js_packages_info. is_runtime_package dep_package_info then
221- get_runtime_module_path dep_module_id current_package_info module_system
222- else
223- begin match module_system with
224- | Commonjs | Esmodule ->
225- (* External package imports: importing from a different package.
226-
227- When dep_pkg.rel_path = "." (dependency uses in-source builds),
228- pkg_rel_path becomes "package_name/." (e.g., "a/."), which would
229- generate invalid imports like "a/./A.res.js" instead of "a/src/A.res.js".
230-
231- We extract the actual source directory from the dependency's .cmj file
232- location and reconstruct the import path correctly.
233- *)
234- if dep_pkg.rel_path = " ." then
235- let cmj_file = dep_module_id.id.name ^ Literals. suffix_cmj in
236- match Config_util. find_opt cmj_file with
237- | Some cmj_path ->
238- (* External packages store .cmj at node_modules/<pkg>/lib/bs/<source_dir>/<module>.cmj
239- Example: /Users/barry/Projects/great-project/node_modules/a/lib/bs/src/A-A.cmj
240- We extract "src" from this path. *)
241- let cmj_dir = Filename. dirname cmj_path in
242- let lib_bs_pattern = " /lib/bs/" in
243- let source_dir =
244- try
245- let idx = String. rindex_from cmj_dir (String. length cmj_dir - 1 ) '/' in
246- let rec find_lib_bs pos =
247- if pos < 0 then None
248- else if Ext_string. starts_with (String. sub cmj_dir pos (String. length cmj_dir - pos)) lib_bs_pattern then
249- Some (pos + String. length lib_bs_pattern)
250- else
251- find_lib_bs (pos - 1 )
260+ else
261+ if Js_packages_info. is_runtime_package dep_package_info then
262+ get_runtime_module_path dep_module_id current_package_info module_system
263+ else begin
264+ match module_system with
265+ | Commonjs | Esmodule ->
266+ (* External package imports: importing from a different package.
267+
268+ When dep_pkg.rel_path = "." (dependency uses in-source builds),
269+ pkg_rel_path becomes "package_name/." (e.g., "a/."), which would
270+ generate invalid imports like "a/./A.res.js" instead of "a/src/A.res.js".
271+
272+ We extract the actual source directory from the dependency's .cmj file
273+ location and reconstruct the import path correctly.
274+ *)
275+ (* External package imports: check if pkg_rel_path ends with "/."
276+ which indicates the dependency uses in-source builds *)
277+ if Ext_string. ends_with dep_pkg.pkg_rel_path " /." then begin
278+ let cmj_file = dep_module_id.id.name ^ Literals. suffix_cmj in
279+ (* Prefer lib/bs over lib/ocaml as lib/bs preserves source directory structure *)
280+ let cmj_opt =
281+ match Config_util. find_opt cmj_file with
282+ | Some path when Ext_string. contain_substring path " /lib/bs/" ->
283+ Some path
284+ | Some ocaml_path ->
285+ (* Found lib/ocaml, derive lib/bs path from it *)
286+ let lib_ocaml_pattern = " /lib/ocaml/" in
287+ let pkg_root =
288+ try
289+ let rec find_lib_ocaml pos =
290+ if pos < 0 then None
291+ else if Ext_string. starts_with (String. sub ocaml_path pos (String. length ocaml_path - pos)) lib_ocaml_pattern then
292+ Some (String. sub ocaml_path 0 pos)
293+ else
294+ find_lib_ocaml (pos - 1 )
295+ in
296+ find_lib_ocaml (String. length ocaml_path - 1 )
297+ with Not_found -> None
252298 in
253- match find_lib_bs idx with
254- | Some start_idx ->
255- String. sub cmj_dir start_idx (String. length cmj_dir - start_idx)
256- | None -> " ."
257- with Not_found -> " ."
299+ (match pkg_root with
300+ | Some root ->
301+ (* The actual cmj file is in lib/bs/src/, not lib/bs/ directly
302+ Try a glob search to find it *)
303+ let rec find_in_dir dir =
304+ let full_path = Filename. concat dir cmj_file in
305+ if Sys. file_exists full_path then Some full_path
306+ else
307+ try
308+ let subdirs = Sys. readdir dir in
309+ Array. fold_left (fun acc subdir ->
310+ match acc with
311+ | Some _ -> acc
312+ | None ->
313+ let sub_path = Filename. concat dir subdir in
314+ if Sys. is_directory sub_path then find_in_dir sub_path
315+ else None
316+ ) None subdirs
317+ with _ -> None
318+ in
319+ let lib_bs_dir = root ^ " /lib/bs" in
320+ (match find_in_dir lib_bs_dir with
321+ | Some bs_path ->
322+ Some bs_path
323+ | None ->
324+ Some ocaml_path)
325+ | None -> Some ocaml_path)
326+ | None -> None
258327 in
259- (* Extract package name from pkg_rel_path: "a/." -> "a" *)
260- let pkg_name =
261- try
262- let idx = String. rindex dep_pkg.pkg_rel_path '/' in
263- String. sub dep_pkg.pkg_rel_path 0 idx
264- with Not_found -> dep_pkg.pkg_rel_path
265- in
266- if source_dir = " ." then
328+ match cmj_opt with
329+ | Some cmj_path ->
330+ (* External packages store .cmj at node_modules/<pkg>/lib/bs/<source_dir>/<module>.cmj
331+ Example: /Users/barry/Projects/rescript/node_modules/a/lib/bs/src/A-A.cmj
332+ We extract "src" from this path. *)
333+ let cmj_dir = Filename. dirname cmj_path in
334+ let lib_bs_pattern = " /lib/bs/" in
335+ let source_dir =
336+ try
337+ let idx = String. rindex_from cmj_dir (String. length cmj_dir - 1 ) '/' in
338+ let rec find_lib_bs pos =
339+ if pos < 0 then None
340+ else if Ext_string. starts_with (String. sub cmj_dir pos (String. length cmj_dir - pos)) lib_bs_pattern then
341+ Some (pos + String. length lib_bs_pattern)
342+ else
343+ find_lib_bs (pos - 1 )
344+ in
345+ match find_lib_bs idx with
346+ | Some start_idx ->
347+ String. sub cmj_dir start_idx (String. length cmj_dir - start_idx)
348+ | None -> " ."
349+ with Not_found -> " ."
350+ in
351+ (* Extract package name from pkg_rel_path: "a/." -> "a" *)
352+ let pkg_name =
353+ String. sub dep_pkg.pkg_rel_path 0 (String. length dep_pkg.pkg_rel_path - 2 )
354+ in
355+ if source_dir = " ." then begin
356+ dep_pkg.pkg_rel_path // js_file
357+ end else begin
358+ let result = pkg_name // source_dir // js_file in
359+ (* Reconstruct: "a" + "src" + "A.res.js" = "a/src/A.res.js" *)
360+ result
361+ end
362+ | None ->
267363 dep_pkg.pkg_rel_path // js_file
268- else
269- (* Reconstruct: "a" + "src" + "A.res.js" = "a/src/A.res.js" *)
270- pkg_name // source_dir // js_file
271- | None ->
364+ end else begin
272365 dep_pkg.pkg_rel_path // js_file
273- else
274- dep_pkg.pkg_rel_path // js_file
366+ end
275367 (* Note we did a post-processing when working on Windows *)
276368 | Es6_global
277369 ->
0 commit comments