summaryrefslogtreecommitdiff
path: root/cli/util/fs.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2023-08-01 20:49:09 -0400
committerGitHub <noreply@github.com>2023-08-02 00:49:09 +0000
commit1cefa831fd74b14121494045a347024502d74e34 (patch)
treecc7791cf674e427fe4165262db416e6c537e99a3 /cli/util/fs.rs
parent36ae37604a0ddab4349df6eb6fafb8ae39fd20fc (diff)
feat(unstable): optional `deno_modules` directory (#19977)
Closes #15633
Diffstat (limited to 'cli/util/fs.rs')
-rw-r--r--cli/util/fs.rs112
1 files changed, 95 insertions, 17 deletions
diff --git a/cli/util/fs.rs b/cli/util/fs.rs
index b17e2d2d9..6c1f304d6 100644
--- a/cli/util/fs.rs
+++ b/cli/util/fs.rs
@@ -26,19 +26,79 @@ use crate::util::progress_bar::ProgressMessagePrompt;
use super::path::specifier_to_file_path;
+/// Writes the file to the file system at a temporary path, then
+/// renames it to the destination in a single sys call in order
+/// to never leave the file system in a corrupted state.
+///
+/// This also handles creating the directory if a NotFound error
+/// occurs.
pub fn atomic_write_file<T: AsRef<[u8]>>(
- filename: &Path,
+ file_path: &Path,
data: T,
mode: u32,
) -> std::io::Result<()> {
- let rand: String = (0..4)
- .map(|_| format!("{:02x}", rand::random::<u8>()))
- .collect();
- let extension = format!("{rand}.tmp");
- let tmp_file = filename.with_extension(extension);
- write_file(&tmp_file, data, mode)?;
- std::fs::rename(tmp_file, filename)?;
- Ok(())
+ fn atomic_write_file_raw(
+ temp_file_path: &Path,
+ file_path: &Path,
+ data: &[u8],
+ mode: u32,
+ ) -> std::io::Result<()> {
+ write_file(temp_file_path, data, mode)?;
+ std::fs::rename(temp_file_path, file_path)?;
+ Ok(())
+ }
+
+ fn add_file_context(file_path: &Path, err: Error) -> Error {
+ Error::new(
+ err.kind(),
+ format!("{:#} (for '{}')", err, file_path.display()),
+ )
+ }
+
+ fn inner(file_path: &Path, data: &[u8], mode: u32) -> std::io::Result<()> {
+ let temp_file_path = {
+ let rand: String = (0..4)
+ .map(|_| format!("{:02x}", rand::random::<u8>()))
+ .collect();
+ let extension = format!("{rand}.tmp");
+ file_path.with_extension(extension)
+ };
+
+ if let Err(write_err) =
+ atomic_write_file_raw(&temp_file_path, file_path, data, mode)
+ {
+ if write_err.kind() == ErrorKind::NotFound {
+ let parent_dir_path = file_path.parent().unwrap();
+ match std::fs::create_dir_all(parent_dir_path) {
+ Ok(()) => {
+ return atomic_write_file_raw(
+ &temp_file_path,
+ file_path,
+ data,
+ mode,
+ )
+ .map_err(|err| add_file_context(file_path, err));
+ }
+ Err(create_err) => {
+ if !parent_dir_path.exists() {
+ return Err(Error::new(
+ create_err.kind(),
+ format!(
+ "{:#} (for '{}')\nCheck the permission of the directory.",
+ create_err,
+ parent_dir_path.display()
+ ),
+ ));
+ }
+ }
+ }
+ }
+ return Err(add_file_context(file_path, write_err));
+ }
+ Ok(())
+ }
+
+ inner(file_path, data.as_ref(), mode)
}
pub fn write_file<T: AsRef<[u8]>>(
@@ -140,6 +200,7 @@ pub struct FileCollector<TFilter: Fn(&Path) -> bool> {
file_filter: TFilter,
ignore_git_folder: bool,
ignore_node_modules: bool,
+ ignore_deno_modules: bool,
}
impl<TFilter: Fn(&Path) -> bool> FileCollector<TFilter> {
@@ -149,6 +210,7 @@ impl<TFilter: Fn(&Path) -> bool> FileCollector<TFilter> {
file_filter,
ignore_git_folder: false,
ignore_node_modules: false,
+ ignore_deno_modules: false,
}
}
@@ -165,6 +227,11 @@ impl<TFilter: Fn(&Path) -> bool> FileCollector<TFilter> {
self
}
+ pub fn ignore_deno_modules(mut self) -> Self {
+ self.ignore_deno_modules = true;
+ self
+ }
+
pub fn ignore_git_folder(mut self) -> Self {
self.ignore_git_folder = true;
self
@@ -203,9 +270,12 @@ impl<TFilter: Fn(&Path) -> bool> FileCollector<TFilter> {
.file_name()
.map(|dir_name| {
let dir_name = dir_name.to_string_lossy().to_lowercase();
- let is_ignored_file = self.ignore_node_modules
- && dir_name == "node_modules"
- || self.ignore_git_folder && dir_name == ".git";
+ let is_ignored_file = match dir_name.as_str() {
+ "node_modules" => self.ignore_node_modules,
+ "deno_modules" => self.ignore_deno_modules,
+ ".git" => self.ignore_git_folder,
+ _ => false,
+ };
// allow the user to opt out of ignoring by explicitly specifying the dir
file != c && is_ignored_file
})
@@ -238,7 +308,8 @@ pub fn collect_specifiers(
let file_collector = FileCollector::new(predicate)
.add_ignore_paths(&files.exclude)
.ignore_git_folder()
- .ignore_node_modules();
+ .ignore_node_modules()
+ .ignore_deno_modules();
let root_path = current_dir()?;
let include_files = if files.include.is_empty() {
@@ -657,10 +728,12 @@ mod tests {
// ├── a.ts
// ├── b.js
// ├── child
- // | ├── node_modules
- // | | └── node_modules.js
+ // | ├── deno_modules
+ // | | └── deno_modules.js
// | ├── git
// | | └── git.js
+ // | ├── node_modules
+ // | | └── node_modules.js
// │ ├── e.mjs
// │ ├── f.mjsx
// │ ├── .foo.TS
@@ -685,6 +758,8 @@ mod tests {
t.write("dir.ts/child/node_modules/node_modules.js", "");
t.create_dir_all("dir.ts/child/.git");
t.write("dir.ts/child/.git/git.js", "");
+ t.create_dir_all("dir.ts/child/deno_modules");
+ t.write("dir.ts/child/deno_modules/deno_modules.js", "");
let ignore_dir_path = root_dir_path.join("ignore");
let ignore_dir_files = ["g.d.ts", ".gitignore"];
@@ -709,6 +784,7 @@ mod tests {
"b.js",
"c.tsx",
"d.jsx",
+ "deno_modules.js",
"e.mjs",
"f.mjsx",
"git.js",
@@ -722,8 +798,10 @@ mod tests {
assert_eq!(file_names, expected);
// test ignoring the .git and node_modules folder
- let file_collector =
- file_collector.ignore_git_folder().ignore_node_modules();
+ let file_collector = file_collector
+ .ignore_git_folder()
+ .ignore_node_modules()
+ .ignore_deno_modules();
let result = file_collector
.collect_files(&[root_dir_path.to_path_buf()])
.unwrap();