2024-01-01 14:58:21 -05:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2020-12-13 13:45:53 -05:00
|
|
|
|
2024-04-17 10:19:55 -04:00
|
|
|
use deno_ast::ModuleSpecifier;
|
2021-11-16 09:02:28 -05:00
|
|
|
use deno_core::anyhow::Context;
|
2024-04-17 10:19:55 -04:00
|
|
|
use deno_core::error::uri_error;
|
2020-12-13 13:45:53 -05:00
|
|
|
use deno_core::error::AnyError;
|
|
|
|
pub use deno_core::normalize_path;
|
2023-01-14 23:18:58 -05:00
|
|
|
use std::path::Path;
|
|
|
|
use std::path::PathBuf;
|
2020-12-13 13:45:53 -05:00
|
|
|
|
2022-08-27 11:20:05 -04:00
|
|
|
#[inline]
|
2020-12-13 13:45:53 -05:00
|
|
|
pub fn resolve_from_cwd(path: &Path) -> Result<PathBuf, AnyError> {
|
2022-08-27 11:20:05 -04:00
|
|
|
if path.is_absolute() {
|
|
|
|
Ok(normalize_path(path))
|
2020-12-13 13:45:53 -05:00
|
|
|
} else {
|
2023-05-10 20:06:59 -04:00
|
|
|
#[allow(clippy::disallowed_methods)]
|
|
|
|
let cwd = std::env::current_dir()
|
|
|
|
.context("Failed to get current working directory")?;
|
2022-12-17 17:20:15 -05:00
|
|
|
Ok(normalize_path(cwd.join(path)))
|
2022-08-27 11:20:05 -04:00
|
|
|
}
|
2020-12-13 13:45:53 -05:00
|
|
|
}
|
|
|
|
|
2024-04-17 10:19:55 -04:00
|
|
|
/// Attempts to convert a specifier to a file path. By default, uses the Url
|
|
|
|
/// crate's `to_file_path()` method, but falls back to try and resolve unix-style
|
|
|
|
/// paths on Windows.
|
|
|
|
pub fn specifier_to_file_path(
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
) -> Result<PathBuf, AnyError> {
|
|
|
|
let result = if specifier.scheme() != "file" {
|
|
|
|
Err(())
|
|
|
|
} else if cfg!(windows) {
|
|
|
|
match specifier.to_file_path() {
|
|
|
|
Ok(path) => Ok(path),
|
|
|
|
Err(()) => {
|
|
|
|
// This might be a unix-style path which is used in the tests even on Windows.
|
|
|
|
// Attempt to see if we can convert it to a `PathBuf`. This code should be removed
|
|
|
|
// once/if https://github.com/servo/rust-url/issues/730 is implemented.
|
|
|
|
if specifier.scheme() == "file"
|
|
|
|
&& specifier.host().is_none()
|
|
|
|
&& specifier.port().is_none()
|
|
|
|
&& specifier.path_segments().is_some()
|
|
|
|
{
|
|
|
|
let path_str = specifier.path();
|
|
|
|
match String::from_utf8(
|
|
|
|
percent_encoding::percent_decode(path_str.as_bytes()).collect(),
|
|
|
|
) {
|
|
|
|
Ok(path_str) => Ok(PathBuf::from(path_str)),
|
|
|
|
Err(_) => Err(()),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
specifier.to_file_path()
|
|
|
|
};
|
|
|
|
match result {
|
|
|
|
Ok(path) => Ok(path),
|
|
|
|
Err(()) => Err(uri_error(format!(
|
|
|
|
"Invalid file path.\n Specifier: {specifier}"
|
|
|
|
))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-13 13:45:53 -05:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
2023-05-10 20:06:59 -04:00
|
|
|
fn current_dir() -> PathBuf {
|
|
|
|
#[allow(clippy::disallowed_methods)]
|
|
|
|
std::env::current_dir().unwrap()
|
|
|
|
}
|
|
|
|
|
2020-12-13 13:45:53 -05:00
|
|
|
#[test]
|
|
|
|
fn resolve_from_cwd_child() {
|
2023-05-10 20:06:59 -04:00
|
|
|
let cwd = current_dir();
|
2020-12-13 13:45:53 -05:00
|
|
|
assert_eq!(resolve_from_cwd(Path::new("a")).unwrap(), cwd.join("a"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn resolve_from_cwd_dot() {
|
2023-05-10 20:06:59 -04:00
|
|
|
let cwd = current_dir();
|
2020-12-13 13:45:53 -05:00
|
|
|
assert_eq!(resolve_from_cwd(Path::new(".")).unwrap(), cwd);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn resolve_from_cwd_parent() {
|
2023-05-10 20:06:59 -04:00
|
|
|
let cwd = current_dir();
|
2020-12-13 13:45:53 -05:00
|
|
|
assert_eq!(resolve_from_cwd(Path::new("a/..")).unwrap(), cwd);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_normalize_path() {
|
|
|
|
assert_eq!(normalize_path(Path::new("a/../b")), PathBuf::from("b"));
|
|
|
|
assert_eq!(normalize_path(Path::new("a/./b/")), PathBuf::from("a/b/"));
|
|
|
|
assert_eq!(
|
|
|
|
normalize_path(Path::new("a/./b/../c")),
|
|
|
|
PathBuf::from("a/c")
|
|
|
|
);
|
|
|
|
|
|
|
|
if cfg!(windows) {
|
|
|
|
assert_eq!(
|
|
|
|
normalize_path(Path::new("C:\\a\\.\\b\\..\\c")),
|
|
|
|
PathBuf::from("C:\\a\\c")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn resolve_from_cwd_absolute() {
|
2023-04-12 19:45:53 -04:00
|
|
|
let expected = Path::new("a");
|
2023-05-10 20:06:59 -04:00
|
|
|
let cwd = current_dir();
|
2023-04-12 19:45:53 -04:00
|
|
|
let absolute_expected = cwd.join(expected);
|
|
|
|
assert_eq!(resolve_from_cwd(expected).unwrap(), absolute_expected);
|
2020-12-13 13:45:53 -05:00
|
|
|
}
|
2024-04-17 10:19:55 -04:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_specifier_to_file_path() {
|
|
|
|
run_success_test("file:///", "/");
|
|
|
|
run_success_test("file:///test", "/test");
|
|
|
|
run_success_test("file:///dir/test/test.txt", "/dir/test/test.txt");
|
|
|
|
run_success_test(
|
|
|
|
"file:///dir/test%20test/test.txt",
|
|
|
|
"/dir/test test/test.txt",
|
|
|
|
);
|
|
|
|
|
|
|
|
fn run_success_test(specifier: &str, expected_path: &str) {
|
|
|
|
let result =
|
|
|
|
specifier_to_file_path(&ModuleSpecifier::parse(specifier).unwrap())
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(result, PathBuf::from(expected_path));
|
|
|
|
}
|
|
|
|
}
|
2020-12-13 13:45:53 -05:00
|
|
|
}
|