Skip to main content

intlayer_swc_plugin/
paths.rs

1//! Path helpers used to build the module specifiers of the injected
2//! dictionary imports.
3
4use pathdiff::diff_paths;
5use std::{borrow::Cow, path::Path};
6
7/// Computes the module specifier for an injected dictionary import: the path
8/// of `dict_file_abs` relative to `from_dir_abs`, using forward slashes and a
9/// leading `./` when the path is not already relative. Falls back to the
10/// absolute path when no relative path exists (e.g. different drives).
11pub fn relative_import_path(dict_file_abs: &Path, from_dir_abs: &Path) -> String {
12    if let Some(relative) = diff_paths(dict_file_abs, from_dir_abs) {
13        let path = relative.to_string_lossy().replace('\\', "/");
14        if path.starts_with('.') {
15            path
16        } else {
17            format!("./{}", path)
18        }
19    } else {
20        dict_file_abs.to_string_lossy().replace('\\', "/")
21    }
22}
23
24/// Whether `path` is already normalised: forward slashes throughout and, if it
25/// carries a drive letter, a lower-case one.
26fn is_normalized(path: &str) -> bool {
27    if path.as_bytes().contains(&b'\\') {
28        return false;
29    }
30
31    !has_upper_case_drive_letter(path)
32}
33
34/// Whether `path` starts with an upper-case Windows drive letter (`C:`).
35fn has_upper_case_drive_letter(path: &str) -> bool {
36    let bytes = path.as_bytes();
37
38    bytes.len() >= 2 && bytes[1] == b':' && bytes[0].is_ascii_uppercase()
39}
40
41/// Normalises a path string to use forward slashes and consistent drive-letter
42/// casing so that [`pathdiff::diff_paths`] works correctly in Wasm / cross-platform
43/// contexts where Windows-style paths may arrive from the JS host.
44///
45/// Borrows when the path is already normalised — the case for every POSIX path,
46/// and the allowlist is re-scanned for each compiled file, so allocating there
47/// would cost one `String` per entry per file.
48pub fn normalize_path(path: &str) -> Cow<'_, str> {
49    if is_normalized(path) {
50        return Cow::Borrowed(path);
51    }
52
53    let mut normalized = path.replace('\\', "/");
54
55    if has_upper_case_drive_letter(&normalized) {
56        normalized[0..1].make_ascii_lowercase();
57    }
58
59    Cow::Owned(normalized)
60}