summaryrefslogtreecommitdiff
path: root/cli/fs_util.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/fs_util.rs')
-rw-r--r--cli/fs_util.rs1209
1 files changed, 0 insertions, 1209 deletions
diff --git a/cli/fs_util.rs b/cli/fs_util.rs
deleted file mode 100644
index a27586da2..000000000
--- a/cli/fs_util.rs
+++ /dev/null
@@ -1,1209 +0,0 @@
-// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-
-use deno_core::anyhow::Context;
-use deno_core::error::uri_error;
-use deno_core::error::AnyError;
-pub use deno_core::normalize_path;
-use deno_core::ModuleSpecifier;
-use deno_runtime::deno_crypto::rand;
-use deno_runtime::deno_node::PathClean;
-use std::borrow::Cow;
-use std::env::current_dir;
-use std::fs::OpenOptions;
-use std::io::Error;
-use std::io::ErrorKind;
-use std::io::Write;
-use std::path::Path;
-use std::path::PathBuf;
-use std::time::Duration;
-use walkdir::WalkDir;
-
-pub fn atomic_write_file<T: AsRef<[u8]>>(
- filename: &Path,
- data: T,
- mode: u32,
-) -> std::io::Result<()> {
- let rand: String = (0..4)
- .map(|_| format!("{:02x}", rand::random::<u8>()))
- .collect();
- let extension = format!("{}.tmp", rand);
- let tmp_file = filename.with_extension(extension);
- write_file(&tmp_file, data, mode)?;
- std::fs::rename(tmp_file, filename)?;
- Ok(())
-}
-
-pub fn write_file<T: AsRef<[u8]>>(
- filename: &Path,
- data: T,
- mode: u32,
-) -> std::io::Result<()> {
- write_file_2(filename, data, true, mode, true, false)
-}
-
-pub fn write_file_2<T: AsRef<[u8]>>(
- filename: &Path,
- data: T,
- update_mode: bool,
- mode: u32,
- is_create: bool,
- is_append: bool,
-) -> std::io::Result<()> {
- let mut file = OpenOptions::new()
- .read(false)
- .write(true)
- .append(is_append)
- .truncate(!is_append)
- .create(is_create)
- .open(filename)?;
-
- if update_mode {
- #[cfg(unix)]
- {
- use std::os::unix::fs::PermissionsExt;
- let mode = mode & 0o777;
- let permissions = PermissionsExt::from_mode(mode);
- file.set_permissions(permissions)?;
- }
- #[cfg(not(unix))]
- let _ = mode;
- }
-
- file.write_all(data.as_ref())
-}
-
-/// Similar to `std::fs::canonicalize()` but strips UNC prefixes on Windows.
-pub fn canonicalize_path(path: &Path) -> Result<PathBuf, Error> {
- let path = path.canonicalize()?;
- #[cfg(windows)]
- return Ok(strip_unc_prefix(path));
- #[cfg(not(windows))]
- return Ok(path);
-}
-
-/// Canonicalizes a path which might be non-existent by going up the
-/// ancestors until it finds a directory that exists, canonicalizes
-/// that path, then adds back the remaining path components.
-///
-/// Note: When using this, you should be aware that a symlink may
-/// subsequently be created along this path by some other code.
-pub fn canonicalize_path_maybe_not_exists(
- path: &Path,
-) -> Result<PathBuf, Error> {
- let path = path.to_path_buf().clean();
- let mut path = path.as_path();
- let mut names_stack = Vec::new();
- loop {
- match canonicalize_path(path) {
- Ok(mut canonicalized_path) => {
- for name in names_stack.into_iter().rev() {
- canonicalized_path = canonicalized_path.join(name);
- }
- return Ok(canonicalized_path);
- }
- Err(err) if err.kind() == ErrorKind::NotFound => {
- names_stack.push(path.file_name().unwrap());
- path = path.parent().unwrap();
- }
- Err(err) => return Err(err),
- }
- }
-}
-
-#[cfg(windows)]
-fn strip_unc_prefix(path: PathBuf) -> PathBuf {
- use std::path::Component;
- use std::path::Prefix;
-
- let mut components = path.components();
- match components.next() {
- Some(Component::Prefix(prefix)) => {
- match prefix.kind() {
- // \\?\device
- Prefix::Verbatim(device) => {
- let mut path = PathBuf::new();
- path.push(format!(r"\\{}\", device.to_string_lossy()));
- path.extend(components.filter(|c| !matches!(c, Component::RootDir)));
- path
- }
- // \\?\c:\path
- Prefix::VerbatimDisk(_) => {
- let mut path = PathBuf::new();
- path.push(prefix.as_os_str().to_string_lossy().replace(r"\\?\", ""));
- path.extend(components);
- path
- }
- // \\?\UNC\hostname\share_name\path
- Prefix::VerbatimUNC(hostname, share_name) => {
- let mut path = PathBuf::new();
- path.push(format!(
- r"\\{}\{}\",
- hostname.to_string_lossy(),
- share_name.to_string_lossy()
- ));
- path.extend(components.filter(|c| !matches!(c, Component::RootDir)));
- path
- }
- _ => path,
- }
- }
- _ => path,
- }
-}
-
-pub fn resolve_from_cwd(path: &Path) -> Result<PathBuf, AnyError> {
- let resolved_path = if path.is_absolute() {
- path.to_owned()
- } else {
- let cwd =
- current_dir().context("Failed to get current working directory")?;
- cwd.join(path)
- };
-
- Ok(normalize_path(&resolved_path))
-}
-
-/// Checks if the path has extension Deno supports.
-pub fn is_supported_ext(path: &Path) -> bool {
- if let Some(ext) = get_extension(path) {
- matches!(
- ext.as_str(),
- "ts" | "tsx" | "js" | "jsx" | "mjs" | "mts" | "cjs" | "cts"
- )
- } else {
- false
- }
-}
-
-/// Checks if the path has a basename and extension Deno supports for tests.
-pub fn is_supported_test_path(path: &Path) -> bool {
- if let Some(name) = path.file_stem() {
- let basename = name.to_string_lossy();
- (basename.ends_with("_test")
- || basename.ends_with(".test")
- || basename == "test")
- && is_supported_ext(path)
- } else {
- false
- }
-}
-
-/// Checks if the path has a basename and extension Deno supports for benches.
-pub fn is_supported_bench_path(path: &Path) -> bool {
- if let Some(name) = path.file_stem() {
- let basename = name.to_string_lossy();
- (basename.ends_with("_bench")
- || basename.ends_with(".bench")
- || basename == "bench")
- && is_supported_ext(path)
- } else {
- false
- }
-}
-
-/// Checks if the path has an extension Deno supports for tests.
-pub fn is_supported_test_ext(path: &Path) -> bool {
- if let Some(ext) = get_extension(path) {
- matches!(
- ext.as_str(),
- "ts"
- | "tsx"
- | "js"
- | "jsx"
- | "mjs"
- | "mts"
- | "cjs"
- | "cts"
- | "md"
- | "mkd"
- | "mkdn"
- | "mdwn"
- | "mdown"
- | "markdown"
- )
- } else {
- false
- }
-}
-
-/// Get the extension of a file in lowercase.
-pub fn get_extension(file_path: &Path) -> Option<String> {
- return file_path
- .extension()
- .and_then(|e| e.to_str())
- .map(|e| e.to_lowercase());
-}
-
-/// Collects file paths that satisfy the given predicate, by recursively walking `files`.
-/// If the walker visits a path that is listed in `ignore`, it skips descending into the directory.
-pub fn collect_files<P>(
- files: &[PathBuf],
- ignore: &[PathBuf],
- predicate: P,
-) -> Result<Vec<PathBuf>, AnyError>
-where
- P: Fn(&Path) -> bool,
-{
- let mut target_files = Vec::new();
-
- // retain only the paths which exist and ignore the rest
- let canonicalized_ignore: Vec<PathBuf> = ignore
- .iter()
- .filter_map(|i| canonicalize_path(i).ok())
- .collect();
-
- for file in files {
- for entry in WalkDir::new(file)
- .into_iter()
- .filter_entry(|e| {
- canonicalize_path(e.path()).map_or(false, |c| {
- !canonicalized_ignore.iter().any(|i| c.starts_with(i))
- })
- })
- .filter_map(|e| match e {
- Ok(e) if !e.file_type().is_dir() && predicate(e.path()) => Some(e),
- _ => None,
- })
- {
- target_files.push(canonicalize_path(entry.path())?)
- }
- }
-
- Ok(target_files)
-}
-
-/// Collects module specifiers that satisfy the given predicate as a file path, by recursively walking `include`.
-/// Specifiers that start with http and https are left intact.
-pub fn collect_specifiers<P>(
- include: Vec<String>,
- ignore: &[PathBuf],
- predicate: P,
-) -> Result<Vec<ModuleSpecifier>, AnyError>
-where
- P: Fn(&Path) -> bool,
-{
- let mut prepared = vec![];
-
- let root_path = current_dir()?;
- for path in include {
- let lowercase_path = path.to_lowercase();
- if lowercase_path.starts_with("http://")
- || lowercase_path.starts_with("https://")
- {
- let url = ModuleSpecifier::parse(&path)?;
- prepared.push(url);
- continue;
- }
-
- let p = if lowercase_path.starts_with("file://") {
- specifier_to_file_path(&ModuleSpecifier::parse(&path)?)?
- } else {
- root_path.join(path)
- };
- let p = normalize_path(&p);
- if p.is_dir() {
- let test_files = collect_files(&[p], ignore, &predicate).unwrap();
- let mut test_files_as_urls = test_files
- .iter()
- .map(|f| ModuleSpecifier::from_file_path(f).unwrap())
- .collect::<Vec<ModuleSpecifier>>();
-
- test_files_as_urls.sort();
- prepared.extend(test_files_as_urls);
- } else {
- let url = ModuleSpecifier::from_file_path(p).unwrap();
- prepared.push(url);
- }
- }
-
- Ok(prepared)
-}
-
-/// Asynchronously removes a directory and all its descendants, but does not error
-/// when the directory does not exist.
-pub async fn remove_dir_all_if_exists(path: &Path) -> std::io::Result<()> {
- let result = tokio::fs::remove_dir_all(path).await;
- match result {
- Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()),
- _ => result,
- }
-}
-
-/// Copies a directory to another directory.
-///
-/// Note: Does not handle symlinks.
-pub fn copy_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> {
- std::fs::create_dir_all(to)
- .with_context(|| format!("Creating {}", to.display()))?;
- let read_dir = std::fs::read_dir(from)
- .with_context(|| format!("Reading {}", from.display()))?;
-
- for entry in read_dir {
- let entry = entry?;
- let file_type = entry.file_type()?;
- let new_from = from.join(entry.file_name());
- let new_to = to.join(entry.file_name());
-
- if file_type.is_dir() {
- copy_dir_recursive(&new_from, &new_to).with_context(|| {
- format!("Dir {} to {}", new_from.display(), new_to.display())
- })?;
- } else if file_type.is_file() {
- std::fs::copy(&new_from, &new_to).with_context(|| {
- format!("Copying {} to {}", new_from.display(), new_to.display())
- })?;
- }
- }
-
- Ok(())
-}
-
-/// Hardlinks the files in one directory to another directory.
-///
-/// Note: Does not handle symlinks.
-pub fn hard_link_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> {
- std::fs::create_dir_all(to)
- .with_context(|| format!("Creating {}", to.display()))?;
- let read_dir = std::fs::read_dir(from)
- .with_context(|| format!("Reading {}", from.display()))?;
-
- for entry in read_dir {
- let entry = entry?;
- let file_type = entry.file_type()?;
- let new_from = from.join(entry.file_name());
- let new_to = to.join(entry.file_name());
-
- if file_type.is_dir() {
- hard_link_dir_recursive(&new_from, &new_to).with_context(|| {
- format!("Dir {} to {}", new_from.display(), new_to.display())
- })?;
- } else if file_type.is_file() {
- // note: chance for race conditions here between attempting to create,
- // then removing, then attempting to create. There doesn't seem to be
- // a way to hard link with overwriting in Rust, but maybe there is some
- // way with platform specific code. The workaround here is to handle
- // scenarios where something else might create or remove files.
- if let Err(err) = std::fs::hard_link(&new_from, &new_to) {
- if err.kind() == ErrorKind::AlreadyExists {
- if let Err(err) = std::fs::remove_file(&new_to) {
- if err.kind() == ErrorKind::NotFound {
- // Assume another process/thread created this hard link to the file we are wanting
- // to remove then sleep a little bit to let the other process/thread move ahead
- // faster to reduce contention.
- std::thread::sleep(Duration::from_millis(10));
- } else {
- return Err(err).with_context(|| {
- format!(
- "Removing file to hard link {} to {}",
- new_from.display(),
- new_to.display()
- )
- });
- }
- }
-
- // Always attempt to recreate the hardlink. In contention scenarios, the other process
- // might have been killed or exited after removing the file, but before creating the hardlink
- if let Err(err) = std::fs::hard_link(&new_from, &new_to) {
- // Assume another process/thread created this hard link to the file we are wanting
- // to now create then sleep a little bit to let the other process/thread move ahead
- // faster to reduce contention.
- if err.kind() == ErrorKind::AlreadyExists {
- std::thread::sleep(Duration::from_millis(10));
- } else {
- return Err(err).with_context(|| {
- format!(
- "Hard linking {} to {}",
- new_from.display(),
- new_to.display()
- )
- });
- }
- }
- } else {
- return Err(err).with_context(|| {
- format!(
- "Hard linking {} to {}",
- new_from.display(),
- new_to.display()
- )
- });
- }
- }
- }
- }
-
- Ok(())
-}
-
-pub fn symlink_dir(oldpath: &Path, newpath: &Path) -> Result<(), AnyError> {
- let err_mapper = |err: Error| {
- Error::new(
- err.kind(),
- format!(
- "{}, symlink '{}' -> '{}'",
- err,
- oldpath.display(),
- newpath.display()
- ),
- )
- };
- #[cfg(unix)]
- {
- use std::os::unix::fs::symlink;
- symlink(oldpath, newpath).map_err(err_mapper)?;
- }
- #[cfg(not(unix))]
- {
- use std::os::windows::fs::symlink_dir;
- symlink_dir(oldpath, newpath).map_err(err_mapper)?;
- }
- Ok(())
-}
-
-/// 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 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
- ))),
- }
-}
-
-/// Ensures a specifier that will definitely be a directory has a trailing slash.
-pub fn ensure_directory_specifier(
- mut specifier: ModuleSpecifier,
-) -> ModuleSpecifier {
- let path = specifier.path();
- if !path.ends_with('/') {
- let new_path = format!("{}/", path);
- specifier.set_path(&new_path);
- }
- specifier
-}
-
-/// Gets the parent of this module specifier.
-pub fn specifier_parent(specifier: &ModuleSpecifier) -> ModuleSpecifier {
- let mut specifier = specifier.clone();
- // don't use specifier.segments() because it will strip the leading slash
- let mut segments = specifier.path().split('/').collect::<Vec<_>>();
- if segments.iter().all(|s| s.is_empty()) {
- return specifier;
- }
- if let Some(last) = segments.last() {
- if last.is_empty() {
- segments.pop();
- }
- segments.pop();
- let new_path = format!("{}/", segments.join("/"));
- specifier.set_path(&new_path);
- }
- specifier
-}
-
-/// `from.make_relative(to)` but with fixes.
-pub fn relative_specifier(
- from: &ModuleSpecifier,
- to: &ModuleSpecifier,
-) -> Option<String> {
- let is_dir = to.path().ends_with('/');
-
- if is_dir && from == to {
- return Some("./".to_string());
- }
-
- // workaround using parent directory until https://github.com/servo/rust-url/pull/754 is merged
- let from = if !from.path().ends_with('/') {
- if let Some(end_slash) = from.path().rfind('/') {
- let mut new_from = from.clone();
- new_from.set_path(&from.path()[..end_slash + 1]);
- Cow::Owned(new_from)
- } else {
- Cow::Borrowed(from)
- }
- } else {
- Cow::Borrowed(from)
- };
-
- // workaround for url crate not adding a trailing slash for a directory
- // it seems to be fixed once a version greater than 2.2.2 is released
- let mut text = from.make_relative(to)?;
- if is_dir && !text.ends_with('/') && to.query().is_none() {
- text.push('/');
- }
-
- Some(if text.starts_with("../") || text.starts_with("./") {
- text
- } else {
- format!("./{}", text)
- })
-}
-
-/// This function checks if input path has trailing slash or not. If input path
-/// has trailing slash it will return true else it will return false.
-pub fn path_has_trailing_slash(path: &Path) -> bool {
- if let Some(path_str) = path.to_str() {
- if cfg!(windows) {
- path_str.ends_with('\\')
- } else {
- path_str.ends_with('/')
- }
- } else {
- false
- }
-}
-
-/// Gets a path with the specified file stem suffix.
-///
-/// Ex. `file.ts` with suffix `_2` returns `file_2.ts`
-pub fn path_with_stem_suffix(path: &Path, suffix: &str) -> PathBuf {
- if let Some(file_name) = path.file_name().map(|f| f.to_string_lossy()) {
- if let Some(file_stem) = path.file_stem().map(|f| f.to_string_lossy()) {
- if let Some(ext) = path.extension().map(|f| f.to_string_lossy()) {
- return if file_stem.to_lowercase().ends_with(".d") {
- path.with_file_name(format!(
- "{}{}.{}.{}",
- &file_stem[..file_stem.len() - ".d".len()],
- suffix,
- // maintain casing
- &file_stem[file_stem.len() - "d".len()..],
- ext
- ))
- } else {
- path.with_file_name(format!("{}{}.{}", file_stem, suffix, ext))
- };
- }
- }
-
- path.with_file_name(format!("{}{}", file_name, suffix))
- } else {
- path.with_file_name(suffix)
- }
-}
-
-/// Gets if the provided character is not supported on all
-/// kinds of file systems.
-pub fn is_banned_path_char(c: char) -> bool {
- matches!(c, '<' | '>' | ':' | '"' | '|' | '?' | '*')
-}
-
-/// Gets a safe local directory name for the provided url.
-///
-/// For example:
-/// https://deno.land:8080/path -> deno.land_8080/path
-pub fn root_url_to_safe_local_dirname(root: &ModuleSpecifier) -> PathBuf {
- fn sanitize_segment(text: &str) -> String {
- text
- .chars()
- .map(|c| if is_banned_segment_char(c) { '_' } else { c })
- .collect()
- }
-
- fn is_banned_segment_char(c: char) -> bool {
- matches!(c, '/' | '\\') || is_banned_path_char(c)
- }
-
- let mut result = String::new();
- if let Some(domain) = root.domain() {
- result.push_str(&sanitize_segment(domain));
- }
- if let Some(port) = root.port() {
- if !result.is_empty() {
- result.push('_');
- }
- result.push_str(&port.to_string());
- }
- let mut result = PathBuf::from(result);
- if let Some(segments) = root.path_segments() {
- for segment in segments.filter(|s| !s.is_empty()) {
- result = result.join(sanitize_segment(segment));
- }
- }
-
- result
-}
-
-/// Gets the total size (in bytes) of a directory.
-pub fn dir_size(path: &Path) -> std::io::Result<u64> {
- let entries = std::fs::read_dir(path)?;
- let mut total = 0;
- for entry in entries {
- let entry = entry?;
- total += match entry.metadata()? {
- data if data.is_dir() => dir_size(&entry.path())?,
- data => data.len(),
- };
- }
- Ok(total)
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use test_util::TempDir;
-
- #[test]
- fn resolve_from_cwd_child() {
- let cwd = current_dir().unwrap();
- assert_eq!(resolve_from_cwd(Path::new("a")).unwrap(), cwd.join("a"));
- }
-
- #[test]
- fn resolve_from_cwd_dot() {
- let cwd = current_dir().unwrap();
- assert_eq!(resolve_from_cwd(Path::new(".")).unwrap(), cwd);
- }
-
- #[test]
- fn resolve_from_cwd_parent() {
- let cwd = current_dir().unwrap();
- 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")
- );
- }
- }
-
- // TODO: Get a good expected value here for Windows.
- #[cfg(not(windows))]
- #[test]
- fn resolve_from_cwd_absolute() {
- let expected = Path::new("/a");
- assert_eq!(resolve_from_cwd(expected).unwrap(), expected);
- }
-
- #[test]
- fn test_is_supported_ext() {
- assert!(!is_supported_ext(Path::new("tests/subdir/redirects")));
- assert!(!is_supported_ext(Path::new("README.md")));
- assert!(is_supported_ext(Path::new("lib/typescript.d.ts")));
- assert!(is_supported_ext(Path::new("testdata/run/001_hello.js")));
- assert!(is_supported_ext(Path::new("testdata/run/002_hello.ts")));
- assert!(is_supported_ext(Path::new("foo.jsx")));
- assert!(is_supported_ext(Path::new("foo.tsx")));
- assert!(is_supported_ext(Path::new("foo.TS")));
- assert!(is_supported_ext(Path::new("foo.TSX")));
- assert!(is_supported_ext(Path::new("foo.JS")));
- assert!(is_supported_ext(Path::new("foo.JSX")));
- assert!(is_supported_ext(Path::new("foo.mjs")));
- assert!(is_supported_ext(Path::new("foo.mts")));
- assert!(is_supported_ext(Path::new("foo.cjs")));
- assert!(is_supported_ext(Path::new("foo.cts")));
- assert!(!is_supported_ext(Path::new("foo.mjsx")));
- }
-
- #[test]
- fn test_is_supported_test_ext() {
- assert!(!is_supported_test_ext(Path::new("tests/subdir/redirects")));
- assert!(is_supported_test_ext(Path::new("README.md")));
- assert!(is_supported_test_ext(Path::new("readme.MD")));
- assert!(is_supported_test_ext(Path::new("lib/typescript.d.ts")));
- assert!(is_supported_test_ext(Path::new(
- "testdata/run/001_hello.js"
- )));
- assert!(is_supported_test_ext(Path::new(
- "testdata/run/002_hello.ts"
- )));
- assert!(is_supported_test_ext(Path::new("foo.jsx")));
- assert!(is_supported_test_ext(Path::new("foo.tsx")));
- assert!(is_supported_test_ext(Path::new("foo.TS")));
- assert!(is_supported_test_ext(Path::new("foo.TSX")));
- assert!(is_supported_test_ext(Path::new("foo.JS")));
- assert!(is_supported_test_ext(Path::new("foo.JSX")));
- assert!(is_supported_test_ext(Path::new("foo.mjs")));
- assert!(is_supported_test_ext(Path::new("foo.mts")));
- assert!(is_supported_test_ext(Path::new("foo.cjs")));
- assert!(is_supported_test_ext(Path::new("foo.cts")));
- assert!(!is_supported_test_ext(Path::new("foo.mjsx")));
- assert!(!is_supported_test_ext(Path::new("foo.jsonc")));
- assert!(!is_supported_test_ext(Path::new("foo.JSONC")));
- assert!(!is_supported_test_ext(Path::new("foo.json")));
- assert!(!is_supported_test_ext(Path::new("foo.JsON")));
- }
-
- #[test]
- fn test_is_supported_test_path() {
- assert!(is_supported_test_path(Path::new(
- "tests/subdir/foo_test.ts"
- )));
- assert!(is_supported_test_path(Path::new(
- "tests/subdir/foo_test.tsx"
- )));
- assert!(is_supported_test_path(Path::new(
- "tests/subdir/foo_test.js"
- )));
- assert!(is_supported_test_path(Path::new(
- "tests/subdir/foo_test.jsx"
- )));
- assert!(is_supported_test_path(Path::new("bar/foo.test.ts")));
- assert!(is_supported_test_path(Path::new("bar/foo.test.tsx")));
- assert!(is_supported_test_path(Path::new("bar/foo.test.js")));
- assert!(is_supported_test_path(Path::new("bar/foo.test.jsx")));
- assert!(is_supported_test_path(Path::new("foo/bar/test.js")));
- assert!(is_supported_test_path(Path::new("foo/bar/test.jsx")));
- assert!(is_supported_test_path(Path::new("foo/bar/test.ts")));
- assert!(is_supported_test_path(Path::new("foo/bar/test.tsx")));
- assert!(!is_supported_test_path(Path::new("README.md")));
- assert!(!is_supported_test_path(Path::new("lib/typescript.d.ts")));
- assert!(!is_supported_test_path(Path::new("notatest.js")));
- assert!(!is_supported_test_path(Path::new("NotAtest.ts")));
- }
-
- #[test]
- fn test_collect_files() {
- fn create_files(dir_path: &Path, files: &[&str]) {
- std::fs::create_dir(dir_path).expect("Failed to create directory");
- for f in files {
- let path = dir_path.join(f);
- std::fs::write(path, "").expect("Failed to create file");
- }
- }
-
- // dir.ts
- // ├── a.ts
- // ├── b.js
- // ├── child
- // │ ├── e.mjs
- // │ ├── f.mjsx
- // │ ├── .foo.TS
- // │ └── README.md
- // ├── c.tsx
- // ├── d.jsx
- // └── ignore
- // ├── g.d.ts
- // └── .gitignore
-
- let t = TempDir::new();
-
- let root_dir_path = t.path().join("dir.ts");
- let root_dir_files = ["a.ts", "b.js", "c.tsx", "d.jsx"];
- create_files(&root_dir_path, &root_dir_files);
-
- let child_dir_path = root_dir_path.join("child");
- let child_dir_files = ["e.mjs", "f.mjsx", ".foo.TS", "README.md"];
- create_files(&child_dir_path, &child_dir_files);
-
- let ignore_dir_path = root_dir_path.join("ignore");
- let ignore_dir_files = ["g.d.ts", ".gitignore"];
- create_files(&ignore_dir_path, &ignore_dir_files);
-
- let result = collect_files(&[root_dir_path], &[ignore_dir_path], |path| {
- // exclude dotfiles
- path
- .file_name()
- .and_then(|f| f.to_str())
- .map_or(false, |f| !f.starts_with('.'))
- })
- .unwrap();
- let expected = [
- "a.ts",
- "b.js",
- "e.mjs",
- "f.mjsx",
- "README.md",
- "c.tsx",
- "d.jsx",
- ];
- for e in expected.iter() {
- assert!(result.iter().any(|r| r.ends_with(e)));
- }
- assert_eq!(result.len(), expected.len());
- }
-
- #[test]
- fn test_collect_specifiers() {
- fn create_files(dir_path: &Path, files: &[&str]) {
- std::fs::create_dir(dir_path).expect("Failed to create directory");
- for f in files {
- let path = dir_path.join(f);
- std::fs::write(path, "").expect("Failed to create file");
- }
- }
-
- // dir.ts
- // ├── a.ts
- // ├── b.js
- // ├── child
- // │ ├── e.mjs
- // │ ├── f.mjsx
- // │ ├── .foo.TS
- // │ └── README.md
- // ├── c.tsx
- // ├── d.jsx
- // └── ignore
- // ├── g.d.ts
- // └── .gitignore
-
- let t = TempDir::new();
-
- let root_dir_path = t.path().join("dir.ts");
- let root_dir_files = ["a.ts", "b.js", "c.tsx", "d.jsx"];
- create_files(&root_dir_path, &root_dir_files);
-
- let child_dir_path = root_dir_path.join("child");
- let child_dir_files = ["e.mjs", "f.mjsx", ".foo.TS", "README.md"];
- create_files(&child_dir_path, &child_dir_files);
-
- let ignore_dir_path = root_dir_path.join("ignore");
- let ignore_dir_files = ["g.d.ts", ".gitignore"];
- create_files(&ignore_dir_path, &ignore_dir_files);
-
- let predicate = |path: &Path| {
- // exclude dotfiles
- path
- .file_name()
- .and_then(|f| f.to_str())
- .map_or(false, |f| !f.starts_with('.'))
- };
-
- let result = collect_specifiers(
- vec![
- "http://localhost:8080".to_string(),
- root_dir_path.to_str().unwrap().to_string(),
- "https://localhost:8080".to_string(),
- ],
- &[ignore_dir_path],
- predicate,
- )
- .unwrap();
-
- let root_dir_url = ModuleSpecifier::from_file_path(
- canonicalize_path(&root_dir_path).unwrap(),
- )
- .unwrap()
- .to_string();
- let expected: Vec<ModuleSpecifier> = [
- "http://localhost:8080",
- &format!("{}/a.ts", root_dir_url),
- &format!("{}/b.js", root_dir_url),
- &format!("{}/c.tsx", root_dir_url),
- &format!("{}/child/README.md", root_dir_url),
- &format!("{}/child/e.mjs", root_dir_url),
- &format!("{}/child/f.mjsx", root_dir_url),
- &format!("{}/d.jsx", root_dir_url),
- "https://localhost:8080",
- ]
- .iter()
- .map(|f| ModuleSpecifier::parse(f).unwrap())
- .collect::<Vec<_>>();
-
- assert_eq!(result, expected);
-
- let scheme = if cfg!(target_os = "windows") {
- "file:///"
- } else {
- "file://"
- };
- let result = collect_specifiers(
- vec![format!(
- "{}{}",
- scheme,
- root_dir_path
- .join("child")
- .to_str()
- .unwrap()
- .replace('\\', "/")
- )],
- &[],
- predicate,
- )
- .unwrap();
-
- let expected: Vec<ModuleSpecifier> = [
- &format!("{}/child/README.md", root_dir_url),
- &format!("{}/child/e.mjs", root_dir_url),
- &format!("{}/child/f.mjsx", root_dir_url),
- ]
- .iter()
- .map(|f| ModuleSpecifier::parse(f).unwrap())
- .collect::<Vec<_>>();
-
- assert_eq!(result, expected);
- }
-
- #[cfg(windows)]
- #[test]
- fn test_strip_unc_prefix() {
- run_test(r"C:\", r"C:\");
- run_test(r"C:\test\file.txt", r"C:\test\file.txt");
-
- run_test(r"\\?\C:\", r"C:\");
- run_test(r"\\?\C:\test\file.txt", r"C:\test\file.txt");
-
- run_test(r"\\.\C:\", r"\\.\C:\");
- run_test(r"\\.\C:\Test\file.txt", r"\\.\C:\Test\file.txt");
-
- run_test(r"\\?\UNC\localhost\", r"\\localhost");
- run_test(r"\\?\UNC\localhost\c$\", r"\\localhost\c$");
- run_test(
- r"\\?\UNC\localhost\c$\Windows\file.txt",
- r"\\localhost\c$\Windows\file.txt",
- );
- run_test(r"\\?\UNC\wsl$\deno.json", r"\\wsl$\deno.json");
-
- run_test(r"\\?\server1", r"\\server1");
- run_test(r"\\?\server1\e$\", r"\\server1\e$\");
- run_test(
- r"\\?\server1\e$\test\file.txt",
- r"\\server1\e$\test\file.txt",
- );
-
- fn run_test(input: &str, expected: &str) {
- assert_eq!(
- strip_unc_prefix(PathBuf::from(input)),
- PathBuf::from(expected)
- );
- }
- }
-
- #[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));
- }
- }
-
- #[test]
- fn test_ensure_directory_specifier() {
- run_test("file:///", "file:///");
- run_test("file:///test", "file:///test/");
- run_test("file:///test/", "file:///test/");
- run_test("file:///test/other", "file:///test/other/");
- run_test("file:///test/other/", "file:///test/other/");
-
- fn run_test(specifier: &str, expected: &str) {
- let result =
- ensure_directory_specifier(ModuleSpecifier::parse(specifier).unwrap());
- assert_eq!(result.to_string(), expected);
- }
- }
-
- #[test]
- fn test_specifier_parent() {
- run_test("file:///", "file:///");
- run_test("file:///test", "file:///");
- run_test("file:///test/", "file:///");
- run_test("file:///test/other", "file:///test/");
- run_test("file:///test/other.txt", "file:///test/");
- run_test("file:///test/other/", "file:///test/");
-
- fn run_test(specifier: &str, expected: &str) {
- let result =
- specifier_parent(&ModuleSpecifier::parse(specifier).unwrap());
- assert_eq!(result.to_string(), expected);
- }
- }
-
- #[test]
- fn test_relative_specifier() {
- let fixtures: Vec<(&str, &str, Option<&str>)> = vec![
- ("file:///from", "file:///to", Some("./to")),
- ("file:///from", "file:///from/other", Some("./from/other")),
- ("file:///from", "file:///from/other/", Some("./from/other/")),
- ("file:///from", "file:///other/from", Some("./other/from")),
- ("file:///from/", "file:///other/from", Some("../other/from")),
- ("file:///from", "file:///other/from/", Some("./other/from/")),
- (
- "file:///from",
- "file:///to/other.txt",
- Some("./to/other.txt"),
- ),
- (
- "file:///from/test",
- "file:///to/other.txt",
- Some("../to/other.txt"),
- ),
- (
- "file:///from/other.txt",
- "file:///to/other.txt",
- Some("../to/other.txt"),
- ),
- (
- "https://deno.land/x/a/b/d.ts",
- "https://deno.land/x/a/b/c.ts",
- Some("./c.ts"),
- ),
- (
- "https://deno.land/x/a/b/d.ts",
- "https://deno.land/x/a/c.ts",
- Some("../c.ts"),
- ),
- (
- "https://deno.land/x/a/b/d.ts",
- "https://deno.land/x/a/b/c/d.ts",
- Some("./c/d.ts"),
- ),
- (
- "https://deno.land/x/a/b/c/",
- "https://deno.land/x/a/b/c/d.ts",
- Some("./d.ts"),
- ),
- (
- "https://deno.land/x/a/b/c/",
- "https://deno.land/x/a/b/c/d/e.ts",
- Some("./d/e.ts"),
- ),
- (
- "https://deno.land/x/a/b/c/f.ts",
- "https://deno.land/x/a/b/c/d/e.ts",
- Some("./d/e.ts"),
- ),
- (
- "https://deno.land/x/a/b/d.ts",
- "https://deno.land/x/a/c.ts?foo=bar",
- Some("../c.ts?foo=bar"),
- ),
- (
- "https://deno.land/x/a/b/d.ts?foo=bar",
- "https://deno.land/x/a/b/c.ts",
- Some("./c.ts"),
- ),
- ("file:///a/b/d.ts", "file:///a/b/c.ts", Some("./c.ts")),
- ("https://deno.land/x/a/b/c.ts", "file:///a/b/c.ts", None),
- (
- "https://deno.land/",
- "https://deno.land/x/a/b/c.ts",
- Some("./x/a/b/c.ts"),
- ),
- (
- "https://deno.land/x/d/e/f.ts",
- "https://deno.land/x/a/b/c.ts",
- Some("../../a/b/c.ts"),
- ),
- ];
- for (from_str, to_str, expected) in fixtures {
- let from = ModuleSpecifier::parse(from_str).unwrap();
- let to = ModuleSpecifier::parse(to_str).unwrap();
- let actual = relative_specifier(&from, &to);
- assert_eq!(
- actual.as_deref(),
- expected,
- "from: \"{}\" to: \"{}\"",
- from_str,
- to_str
- );
- }
- }
-
- #[test]
- fn test_path_has_trailing_slash() {
- #[cfg(not(windows))]
- {
- run_test("/Users/johndoe/Desktop/deno-project/target/", true);
- run_test(r"/Users/johndoe/deno-project/target//", true);
- run_test("/Users/johndoe/Desktop/deno-project", false);
- run_test(r"/Users/johndoe/deno-project\", false);
- }
-
- #[cfg(windows)]
- {
- run_test(r"C:\test\deno-project\", true);
- run_test(r"C:\test\deno-project\\", true);
- run_test(r"C:\test\file.txt", false);
- run_test(r"C:\test\file.txt/", false);
- }
-
- fn run_test(path_str: &str, expected: bool) {
- let path = Path::new(path_str);
- let result = path_has_trailing_slash(path);
- assert_eq!(result, expected);
- }
- }
-
- #[test]
- fn test_path_with_stem_suffix() {
- assert_eq!(
- path_with_stem_suffix(&PathBuf::from("/"), "_2"),
- PathBuf::from("/_2")
- );
- assert_eq!(
- path_with_stem_suffix(&PathBuf::from("/test"), "_2"),
- PathBuf::from("/test_2")
- );
- assert_eq!(
- path_with_stem_suffix(&PathBuf::from("/test.txt"), "_2"),
- PathBuf::from("/test_2.txt")
- );
- assert_eq!(
- path_with_stem_suffix(&PathBuf::from("/test/subdir"), "_2"),
- PathBuf::from("/test/subdir_2")
- );
- assert_eq!(
- path_with_stem_suffix(&PathBuf::from("/test/subdir.other.txt"), "_2"),
- PathBuf::from("/test/subdir.other_2.txt")
- );
- assert_eq!(
- path_with_stem_suffix(&PathBuf::from("/test.d.ts"), "_2"),
- PathBuf::from("/test_2.d.ts")
- );
- assert_eq!(
- path_with_stem_suffix(&PathBuf::from("/test.D.TS"), "_2"),
- PathBuf::from("/test_2.D.TS")
- );
- assert_eq!(
- path_with_stem_suffix(&PathBuf::from("/test.d.mts"), "_2"),
- PathBuf::from("/test_2.d.mts")
- );
- assert_eq!(
- path_with_stem_suffix(&PathBuf::from("/test.d.cts"), "_2"),
- PathBuf::from("/test_2.d.cts")
- );
- }
-}