diff --git a/rust_port/crates/flow_common/src/files.rs b/rust_port/crates/flow_common/src/files.rs index 94f6b4e3107..90dc7f3e705 100644 --- a/rust_port/crates/flow_common/src/files.rs +++ b/rust_port/crates/flow_common/src/files.rs @@ -908,11 +908,12 @@ pub fn expand_project_root_token(root: &Path, s: &str) -> String { pub fn expand_project_root_token_as_relative(s: &str) -> String { let s = s.replace(PROJECT_ROOT_TOKEN, ""); - if s.starts_with('/') || s.starts_with('\\') { + let s = if s.starts_with('/') || s.starts_with('\\') { s[1..].to_string() } else { s - } + }; + normalize_filename_dir_sep(&s).into_owned() } pub fn expand_builtin_root_token(flowlib_dir: &Path, s: &str) -> String { diff --git a/rust_port/crates/flow_parser/src/file_key.rs b/rust_port/crates/flow_parser/src/file_key.rs index aea4c899fd1..d76f709eda3 100644 --- a/rust_port/crates/flow_parser/src/file_key.rs +++ b/rust_port/crates/flow_parser/src/file_key.rs @@ -317,12 +317,14 @@ fn relative_path_from(root: &str, path: &str) -> String { result.join("/") } +// The result always uses '/' separators for cross-platform consistency +// (saved state generated on Linux must work on Windows and vice versa). pub fn strip_project_root(path: &str) -> String { let guard = PROJECT_ROOT.read().unwrap(); match guard.as_deref() { Some(root) => { if let Some(stripped) = path.strip_prefix(root) { - stripped.to_string() + normalize_dir_sep_with(std::path::MAIN_SEPARATOR, stripped) } else if !is_relative(path) { relative_path_from(root, path) } else { @@ -437,7 +439,9 @@ impl FileKey { let guard = FLOWLIB_ROOT.read().unwrap(); let suffix = match guard.as_deref() { Some(fl) if !fl.is_empty() && path.starts_with(fl) => { - format!("{}{}", FLOWLIB_MARKER, strip_prefix(fl, path)) + let stripped = + normalize_dir_sep_with(std::path::MAIN_SEPARATOR, strip_prefix(fl, path)); + format!("{}{}", FLOWLIB_MARKER, stripped) } _ => { drop(guard); diff --git a/src/common/files.ml b/src/common/files.ml index 65b5e119302..3a777dec68c 100644 --- a/src/common/files.ml +++ b/src/common/files.ml @@ -1031,10 +1031,13 @@ let expand_project_root_token_as_absolute ~root = let expand_project_root_token_as_relative str = let s = str |> Str.split_delim project_root_token |> String.concat "" in - if String.length s > 0 && (s.[0] = '/' || s.[0] = '\\') then - String.sub s 1 (String.length s - 1) - else - s + let s = + if String.length s > 0 && (s.[0] = '/' || s.[0] = '\\') then + String.sub s 1 (String.length s - 1) + else + s + in + Sys_utils.normalize_filename_dir_sep s let expand_builtin_root_token ~flowlib_dir = let flowlib_dir_str = File_path.to_string flowlib_dir |> Sys_utils.normalize_filename_dir_sep in diff --git a/src/parser/file_key.ml b/src/parser/file_key.ml index ccf9d193ad1..318cac1bd18 100644 --- a/src/parser/file_key.ml +++ b/src/parser/file_key.ml @@ -272,12 +272,15 @@ let relative_path_from ?(dir_sep = Filename.dir_sep) root path = compute a relative path from the root — this ensures all suffixes are portable relative paths, which is critical for saved state portability across machines with different root paths. - If the root has not been set yet, returns the path unchanged. *) + If the root has not been set yet, returns the path unchanged. + The result always uses '/' separators for cross-platform consistency + (saved state generated on Linux must work on Windows and vice versa). *) let strip_project_root path = match !project_root with | Some root -> if String.starts_with ~prefix:root path then - String.sub path (String.length root) (String.length path - String.length root) + normalize_dir_sep + (String.sub path (String.length root) (String.length path - String.length root)) else if not (Filename.is_relative path) then relative_path_from root path else @@ -296,7 +299,7 @@ let resource_file_of_absolute path = ResourceFile (strip_project_root path) let lib_file_of_absolute path = match !flowlib_root with | Some fl when String.length fl > 0 && String.starts_with ~prefix:fl path -> - LibFile (flowlib_marker ^ strip_prefix fl path) + LibFile (flowlib_marker ^ normalize_dir_sep (strip_prefix fl path)) | _ -> LibFile (strip_project_root path) module For_tests = struct diff --git a/src/parsing/parsing_service_js.ml b/src/parsing/parsing_service_js.ml index f412295d6d3..7d92d045984 100644 --- a/src/parsing/parsing_service_js.ml +++ b/src/parsing/parsing_service_js.ml @@ -581,4 +581,12 @@ let ensure_parsed ~reader options workers files = workers next in + let not_found = + if Sys.win32 then + FilenameSet.filter + (fun f -> String.length (File_key.to_string f) < 248) + not_found + else + not_found + in Lwt.return (FilenameSet.union changed not_found) diff --git a/src/services/module/module_js.ml b/src/services/module/module_js.ml index 5a962cbd843..60bc0216de2 100644 --- a/src/services/module/module_js.ml +++ b/src/services/module/module_js.ml @@ -581,7 +581,9 @@ module Node = struct let applicable = Base.Option.value_map applicable_dirname_opt - ~f:(fun prefix -> Files.is_prefix prefix (File_key.suffix importing_file)) + ~f:(fun prefix -> + let suffix = File_key.suffix importing_file in + suffix = prefix || String.starts_with ~prefix:(prefix ^ "/") suffix) ~default:true in if applicable then