From 5310e4485a2d91a863f4ac07db607209887b2d9c Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 5 Mar 2024 18:40:17 -0500 Subject: [PATCH 01/10] fix(publish): make include and exclude work --- Cargo.lock | 2 - Cargo.toml | 3 + cli/args/mod.rs | 2 +- cli/lsp/config.rs | 17 ++-- cli/lsp/diagnostics.rs | 5 +- cli/lsp/documents.rs | 11 ++- cli/lsp/language_server.rs | 6 +- cli/tools/bench/mod.rs | 7 +- cli/tools/registry/tar.rs | 177 +++++++++++++++---------------------- cli/tools/test/mod.rs | 7 +- cli/util/fs.rs | 128 +++++++++++++++++++++++++-- 11 files changed, 236 insertions(+), 129 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2faf17d449e1a5..4e31f26e28cf99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1233,8 +1233,6 @@ dependencies = [ [[package]] name = "deno_config" version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebbc05e20df2d5b8562205f9b0c296bc528e833b0de126d489781952e13d939f" dependencies = [ "anyhow", "glob", diff --git a/Cargo.toml b/Cargo.toml index b2f6f4fa3c5b87..04e6f119470517 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -359,3 +359,6 @@ opt-level = 3 opt-level = 3 [profile.release.package.base64-simd] opt-level = 3 + +[patch.crates-io] +deno_config = { path = "../deno_config" } diff --git a/cli/args/mod.rs b/cli/args/mod.rs index a4904d39dcf736..b02261faf0b0c8 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -1252,7 +1252,7 @@ impl CliOptions { pub fn resolve_config_excludes(&self) -> Result { let maybe_config_files = if let Some(config_file) = &self.maybe_config_file { - config_file.to_files_config()? + Some(config_file.to_files_config()?) } else { None }; diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 3d24c8c20587a9..77dfda8f6d9d4b 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -747,7 +747,10 @@ impl ConfigSnapshot { ) -> bool { if let Some(cf) = &self.config_file { if let Some(options) = cf.to_test_config().ok().flatten() { - if !options.files.matches_specifier(specifier) { + if !options + .files + .matches_specifier(specifier, deno_config::glob::PathKind::File) + { return false; } } @@ -1043,7 +1046,10 @@ impl Config { ) -> bool { if let Some(cf) = self.maybe_config_file() { if let Some(options) = cf.to_test_config().ok().flatten() { - if !options.files.matches_specifier(specifier) { + if !options + .files + .matches_specifier(specifier, deno_config::glob::PathKind::File) + { return false; } } @@ -1083,7 +1089,7 @@ impl Config { pub fn get_disabled_paths(&self) -> PathOrPatternSet { let mut path_or_patterns = vec![]; if let Some(cf) = self.maybe_config_file() { - if let Some(files) = cf.to_files_config().ok().flatten() { + if let Some(files) = cf.to_files_config().ok() { for path in files.exclude.into_path_or_patterns() { path_or_patterns.push(path); } @@ -1177,8 +1183,9 @@ fn specifier_enabled( workspace_folders: &[(Url, lsp::WorkspaceFolder)], ) -> bool { if let Some(cf) = config_file { - if let Some(files) = cf.to_files_config().ok().flatten() { - if !files.matches_specifier(specifier) { + if let Some(files) = cf.to_files_config().ok() { + if !files.matches_specifier(specifier, deno_config::glob::PathKind::File) + { return false; } } diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index fe690483027822..0f1f01763802b8 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -845,7 +845,10 @@ fn generate_document_lint_diagnostics( if !config.specifier_enabled(document.specifier()) { return Vec::new(); } - if !lint_options.files.matches_specifier(document.specifier()) { + if !lint_options + .files + .matches_specifier(document.specifier(), deno_config::glob::PathKind::File) + { return Vec::new(); } match document.maybe_parsed_source() { diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index 21a613bfaa963f..10881512f8cdce 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -1348,7 +1348,7 @@ impl Documents { Cow::Owned(p.to_string_lossy().to_string()) } PathOrPattern::RemoteUrl(p) => Cow::Borrowed(p.as_str()), - PathOrPattern::Pattern(p) => Cow::Borrowed(p.as_str()), + PathOrPattern::Pattern(p) => p.as_str(), }) .collect::>(); // ensure these are sorted so the hashing is deterministic @@ -2075,8 +2075,13 @@ impl Iterator for PreloadDocumentFinder { if let Ok(entry) = entry { let path = entry.path(); if let Ok(file_type) = entry.file_type() { - if file_patterns.matches_path(&path) { - if file_type.is_dir() && is_discoverable_dir(&path) { + let is_dir = file_type.is_dir(); + let path_kind = match is_dir { + true => deno_config::glob::PathKind::Directory, + false => deno_config::glob::PathKind::File, + }; + if file_patterns.matches_path(&path, path_kind) { + if is_dir && is_discoverable_dir(&path) { self.pending_entries.push_back(PendingEntry::Dir( path.to_path_buf(), file_patterns.clone(), diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index b108eb54eae549..ff554eef83268e 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1771,7 +1771,11 @@ impl Inner { .url_map .normalize_url(¶ms.text_document.uri, LspUrlKind::File); // skip formatting any files ignored by the config file - if !self.fmt_options.files.matches_specifier(&specifier) { + if !self + .fmt_options + .files + .matches_specifier(&specifier, deno_config::glob::PathKind::File) + { return Ok(None); } let document = match self.documents.get(&specifier) { diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs index 2f0d59f49dbc9a..74a66121ea0fba 100644 --- a/cli/tools/bench/mod.rs +++ b/cli/tools/bench/mod.rs @@ -20,6 +20,7 @@ use crate::version::get_user_agent; use crate::worker::CliMainWorkerFactory; use deno_config::glob::FilePatterns; +use deno_config::glob::PathGlobMatch; use deno_config::glob::PathOrPattern; use deno_core::error::generic_error; use deno_core::error::AnyError; @@ -408,8 +409,10 @@ fn is_supported_bench_path(path: &Path, patterns: &FilePatterns) -> bool { .map(|p| { p.inner().iter().any(|p| match p { PathOrPattern::Path(p) => p == path, - PathOrPattern::RemoteUrl(_) => true, - PathOrPattern::Pattern(p) => p.matches_path(path), + PathOrPattern::RemoteUrl(_) => false, + PathOrPattern::Pattern(p) => { + p.matches_path(path) == PathGlobMatch::Matched + } }) }) .unwrap_or(false); diff --git a/cli/tools/registry/tar.rs b/cli/tools/registry/tar.rs index d24d8abaa0654a..b5c5980d35903d 100644 --- a/cli/tools/registry/tar.rs +++ b/cli/tools/registry/tar.rs @@ -3,12 +3,9 @@ use bytes::Bytes; use deno_ast::MediaType; use deno_config::glob::FilePatterns; -use deno_config::glob::PathOrPattern; use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::url::Url; -use ignore::overrides::OverrideBuilder; -use ignore::WalkBuilder; use sha2::Digest; use std::collections::HashSet; use std::fmt::Write as FmtWrite; @@ -18,6 +15,7 @@ use tar::Header; use crate::cache::LazyGraphSourceParser; use crate::tools::registry::paths::PackagePath; +use crate::util::fs::FileCollector; use super::diagnostics::PublishDiagnostic; use super::diagnostics::PublishDiagnosticsCollector; @@ -45,75 +43,47 @@ pub fn create_gzipped_tarball( unfurler: &SpecifierUnfurler, file_patterns: Option, ) -> Result { + let file_patterns = file_patterns + .unwrap_or_else(|| FilePatterns::new_with_base(dir.to_path_buf())); let mut tar = TarGzArchive::new(); let mut files = vec![]; - let mut paths = HashSet::new(); - - let mut ob = OverrideBuilder::new(dir); - ob.add("!.git")?.add("!node_modules")?.add("!.DS_Store")?; - - for pattern in file_patterns.as_ref().iter().flat_map(|p| p.include.iter()) { - for path_or_pat in pattern.inner() { - match path_or_pat { - PathOrPattern::Path(p) => ob.add(p.to_str().unwrap())?, - PathOrPattern::Pattern(p) => ob.add(p.as_str())?, - PathOrPattern::RemoteUrl(_) => continue, - }; - } - } - - let overrides = ob.build()?; - - let iterator = WalkBuilder::new(dir) - .follow_links(false) - .require_git(false) - .git_ignore(true) - .git_global(true) - .git_exclude(true) - .overrides(overrides) - .filter_entry(move |entry| { - let matches_pattern = file_patterns - .as_ref() - .map(|p| p.matches_path(entry.path())) - .unwrap_or(true); - matches_pattern - }) - .build(); + let iter_paths = FileCollector::new(|path, _| { + path.file_name().map(|s| s != ".DS_Store").unwrap_or(true) + }) + .ignore_git_folder() + .ignore_node_modules() + .ignore_vendor_folder() + .use_gitignore() + .collect_file_patterns(file_patterns)?; - for entry in iterator { - let entry = entry?; + let mut paths = HashSet::with_capacity(iter_paths.len()); - let path = entry.path(); - let Some(file_type) = entry.file_type() else { - // entry doesn’t have a file type if it corresponds to stdin. + for path in iter_paths { + let Ok(specifier) = Url::from_file_path(&path) else { + diagnostics_collector + .to_owned() + .push(PublishDiagnostic::InvalidPath { + path: path.to_path_buf(), + message: "unable to convert path to url".to_string(), + }); continue; }; - let Ok(specifier) = Url::from_file_path(path) else { + let Ok(relative_path) = path.strip_prefix(dir) else { diagnostics_collector .to_owned() .push(PublishDiagnostic::InvalidPath { path: path.to_path_buf(), - message: "unable to convert path to url".to_string(), + message: "path is not in publish directory".to_string(), }); continue; }; - if file_type.is_file() { - let Ok(relative_path) = path.strip_prefix(dir) else { - diagnostics_collector - .to_owned() - .push(PublishDiagnostic::InvalidPath { - path: path.to_path_buf(), - message: "path is not in publish directory".to_string(), - }); - continue; - }; - - let path_str = relative_path.components().fold( - "".to_string(), - |mut path, component| { + let path_str = + relative_path + .components() + .fold("".to_string(), |mut path, component| { path.push('/'); match component { std::path::Component::Normal(normal) => { @@ -124,66 +94,65 @@ pub fn create_gzipped_tarball( _ => unreachable!(), } path - }, - ); + }); - match PackagePath::new(path_str.clone()) { - Ok(package_path) => { - if !paths.insert(package_path) { - diagnostics_collector.to_owned().push( - PublishDiagnostic::DuplicatePath { - path: path.to_path_buf(), - }, - ); - } - } - Err(err) => { + match PackagePath::new(path_str.clone()) { + Ok(package_path) => { + if !paths.insert(package_path) { diagnostics_collector.to_owned().push( - PublishDiagnostic::InvalidPath { + PublishDiagnostic::DuplicatePath { path: path.to_path_buf(), - message: err.to_string(), }, ); } } - - let content = resolve_content_maybe_unfurling( - path, - &specifier, - unfurler, - source_parser, - diagnostics_collector, - )?; - - let media_type = MediaType::from_specifier(&specifier); - if matches!(media_type, MediaType::Jsx | MediaType::Tsx) { - diagnostics_collector.push(PublishDiagnostic::UnsupportedJsxTsx { - specifier: specifier.clone(), - }); + Err(err) => { + diagnostics_collector + .to_owned() + .push(PublishDiagnostic::InvalidPath { + path: path.to_path_buf(), + message: err.to_string(), + }); } + } - files.push(PublishableTarballFile { - path_str: path_str.clone(), + let content = resolve_content_maybe_unfurling( + &path, + &specifier, + unfurler, + source_parser, + diagnostics_collector, + )?; + + let media_type = MediaType::from_specifier(&specifier); + if matches!(media_type, MediaType::Jsx | MediaType::Tsx) { + diagnostics_collector.push(PublishDiagnostic::UnsupportedJsxTsx { specifier: specifier.clone(), - // This hash string matches the checksum computed by registry - hash: format!("sha256-{:x}", sha2::Sha256::digest(&content)), - size: content.len(), - }); - tar - .add_file(format!(".{}", path_str), &content) - .with_context(|| { - format!("Unable to add file to tarball '{}'", entry.path().display()) - })?; - } else if !file_type.is_dir() { - diagnostics_collector.push(PublishDiagnostic::UnsupportedFileType { - specifier, - kind: if file_type.is_symlink() { - "symlink".to_owned() - } else { - format!("{file_type:?}") - }, }); } + + files.push(PublishableTarballFile { + path_str: path_str.clone(), + specifier: specifier.clone(), + // This hash string matches the checksum computed by registry + hash: format!("sha256-{:x}", sha2::Sha256::digest(&content)), + size: content.len(), + }); + tar + .add_file(format!(".{}", path_str), &content) + .with_context(|| { + format!("Unable to add file to tarball '{}'", path.display()) + })?; + + // todo: handle surfacing this + // diagnostics_collector.push(PublishDiagnostic::UnsupportedFileType { + // specifier, + // kind: if file_type.is_symlink() { + // "symlink".to_owned() + // } else { + // format!("{file_type:?}") + // }, + // }); } let v = tar.finish().context("Unable to finish tarball")?; diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index eb0cdd4cefffab..1f7375dcd65077 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -25,6 +25,7 @@ use deno_ast::swc::common::comments::CommentKind; use deno_ast::MediaType; use deno_ast::SourceRangedForSpanned; use deno_config::glob::FilePatterns; +use deno_config::glob::PathGlobMatch; use deno_config::glob::PathOrPattern; use deno_core::anyhow; use deno_core::anyhow::bail; @@ -1357,8 +1358,10 @@ fn is_supported_test_path_predicate( .map(|p| { p.inner().iter().any(|p| match p { PathOrPattern::Path(p) => p == path, - PathOrPattern::RemoteUrl(_) => true, - PathOrPattern::Pattern(p) => p.matches_path(path), + PathOrPattern::RemoteUrl(_) => false, + PathOrPattern::Pattern(p) => { + p.matches_path(path) == PathGlobMatch::Matched + } }) }) .unwrap_or(false); diff --git a/cli/util/fs.rs b/cli/util/fs.rs index c81686f9593f9f..41c41683bf382b 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -1,5 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::cell::RefCell; +use std::collections::HashMap; use std::collections::HashSet; use std::env::current_dir; use std::fmt::Write as FmtWrite; @@ -9,6 +11,7 @@ use std::io::ErrorKind; use std::io::Write; use std::path::Path; use std::path::PathBuf; +use std::rc::Rc; use std::sync::Arc; use std::time::Duration; use walkdir::WalkDir; @@ -244,6 +247,77 @@ pub fn resolve_from_cwd(path: &Path) -> Result { Ok(normalize_path(resolved_path)) } +struct DirGitIgnores { + current: Option>, + parent: Option>, +} + +impl DirGitIgnores { + pub fn is_ignored(&self, path: &Path, is_dir: bool) -> bool { + let mut is_ignored = false; + if let Some(parent) = &self.parent { + is_ignored = parent.is_ignored(path, is_dir); + } + if let Some(current) = &self.current { + match current.matched(path, is_dir) { + ignore::Match::None => {} + ignore::Match::Ignore(_) => { + is_ignored = true; + } + ignore::Match::Whitelist(_) => { + is_ignored = false; + } + } + } + is_ignored + } +} + +struct GitIgnoreTree { + fs: Arc, + // todo(THIS PR): I think refcell can be removed + ignores: RefCell>>>, +} + +impl GitIgnoreTree { + pub fn get_resolved_git_ignore( + &self, + dir_path: &Path, + ) -> Option> { + let maybe_resolved = self.ignores.borrow().get(dir_path).cloned(); + if let Some(resolved) = maybe_resolved { + resolved + } else { + // todo(THIS PR): stop searching when the root .git folder is found + let parent = dir_path + .parent() + .and_then(|parent| self.get_resolved_git_ignore(parent)); + let current = self + .fs + .read_text_file_sync(&dir_path.join(".gitignore")) + .ok() + .and_then(|text| { + let mut builder = ignore::gitignore::GitignoreBuilder::new(dir_path); + for line in text.lines() { + builder.add_line(None, line).ok()?; + } + let gitignore = builder.build().ok()?; + Some(Rc::new(gitignore)) + }); + let resolved = if parent.is_none() && current.is_none() { + None + } else { + Some(Rc::new(DirGitIgnores { current, parent })) + }; + self + .ignores + .borrow_mut() + .insert(dir_path.to_owned(), resolved.clone()); + resolved + } + } +} + /// 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 struct FileCollector bool> { @@ -251,6 +325,7 @@ pub struct FileCollector bool> { ignore_git_folder: bool, ignore_node_modules: bool, ignore_vendor_folder: bool, + use_gitignore: bool, } impl bool> FileCollector { @@ -260,6 +335,7 @@ impl bool> FileCollector { ignore_git_folder: false, ignore_node_modules: false, ignore_vendor_folder: false, + use_gitignore: false, } } @@ -278,10 +354,53 @@ impl bool> FileCollector { self } + pub fn use_gitignore(mut self) -> Self { + self.use_gitignore = true; + self + } + pub fn collect_file_patterns( &self, file_patterns: FilePatterns, ) -> Result, AnyError> { + fn is_pattern_matched( + maybe_git_ignores: &Option, + path: &PathBuf, + is_dir: bool, + file_patterns: &FilePatterns, + ) -> bool { + use deno_config::glob::FilePatternsMatch; + + let path_kind = match is_dir { + true => deno_config::glob::PathKind::Directory, + false => deno_config::glob::PathKind::File, + }; + match file_patterns.matches_path_detail(path, path_kind) { + FilePatternsMatch::Passed => { + // check gitignore + let is_gitignored = maybe_git_ignores + .as_ref() + .and_then(|git_ignores| { + git_ignores + .get_resolved_git_ignore(path) + .map(|resolved| resolved.is_ignored(path, is_dir)) + }) + .unwrap_or(false); + !is_gitignored + } + FilePatternsMatch::PassedOptedOutExclude => true, + FilePatternsMatch::Excluded => false, + } + } + + let maybe_git_ignores = if self.use_gitignore { + Some(GitIgnoreTree { + fs: Arc::new(deno_runtime::deno_fs::RealFs), + ignores: RefCell::new(HashMap::new()), + }) + } else { + None + }; let mut target_files = Vec::new(); let mut visited_paths = HashSet::new(); let file_patterns_by_base = file_patterns.split_by_base(); @@ -300,14 +419,7 @@ impl bool> FileCollector { let file_type = e.file_type(); let is_dir = file_type.is_dir(); let c = e.path().to_path_buf(); - if file_patterns.exclude.matches_path(&c) - || !is_dir - && !file_patterns - .include - .as_ref() - .map(|i| i.matches_path(&c)) - .unwrap_or(true) - { + if !is_pattern_matched(&maybe_git_ignores, &c, is_dir, &file_patterns) { if is_dir { iterator.skip_current_dir(); } From 0f68e2fbb91421955293b363362d7474a93ad079 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 6 Mar 2024 15:37:13 -0500 Subject: [PATCH 02/10] Update to latest deno_config and fix some publish tests --- cli/args/mod.rs | 2 +- cli/lsp/config.rs | 13 +++---------- cli/lsp/diagnostics.rs | 5 +---- cli/lsp/language_server.rs | 6 +----- cli/tools/bench/mod.rs | 15 ++++++++------- cli/tools/coverage/mod.rs | 7 ++----- cli/tools/doc.rs | 2 +- cli/tools/fmt.rs | 2 +- cli/tools/lint/mod.rs | 2 +- cli/tools/registry/tar.rs | 18 ++++++++++++++++-- cli/tools/test/mod.rs | 23 +++++++++++------------ cli/util/fs.rs | 29 ++++++++++++++++++++--------- tests/integration/publish_tests.rs | 3 ++- 13 files changed, 68 insertions(+), 59 deletions(-) diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 446b1038291f42..8e5d46e183ae81 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -1913,7 +1913,7 @@ mod test { ) .unwrap(); - let mut files = FileCollector::new(|_, _| true) + let mut files = FileCollector::new(|_| true) .ignore_git_folder() .ignore_node_modules() .ignore_vendor_folder() diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 77dfda8f6d9d4b..2d8f83d9c16e27 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -747,10 +747,7 @@ impl ConfigSnapshot { ) -> bool { if let Some(cf) = &self.config_file { if let Some(options) = cf.to_test_config().ok().flatten() { - if !options - .files - .matches_specifier(specifier, deno_config::glob::PathKind::File) - { + if !options.files.matches_specifier(specifier) { return false; } } @@ -1046,10 +1043,7 @@ impl Config { ) -> bool { if let Some(cf) = self.maybe_config_file() { if let Some(options) = cf.to_test_config().ok().flatten() { - if !options - .files - .matches_specifier(specifier, deno_config::glob::PathKind::File) - { + if !options.files.matches_specifier(specifier) { return false; } } @@ -1184,8 +1178,7 @@ fn specifier_enabled( ) -> bool { if let Some(cf) = config_file { if let Some(files) = cf.to_files_config().ok() { - if !files.matches_specifier(specifier, deno_config::glob::PathKind::File) - { + if !files.matches_specifier(specifier) { return false; } } diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 0f1f01763802b8..fe690483027822 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -845,10 +845,7 @@ fn generate_document_lint_diagnostics( if !config.specifier_enabled(document.specifier()) { return Vec::new(); } - if !lint_options - .files - .matches_specifier(document.specifier(), deno_config::glob::PathKind::File) - { + if !lint_options.files.matches_specifier(document.specifier()) { return Vec::new(); } match document.maybe_parsed_source() { diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 71c893c9c7b8b7..3555a0545285df 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1785,11 +1785,7 @@ impl Inner { .url_map .normalize_url(¶ms.text_document.uri, LspUrlKind::File); // skip formatting any files ignored by the config file - if !self - .fmt_options - .files - .matches_specifier(&specifier, deno_config::glob::PathKind::File) - { + if !self.fmt_options.files.matches_specifier(&specifier) { return Ok(None); } let document = match self.documents.get(&specifier) { diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs index 74a66121ea0fba..345a9f903b7c12 100644 --- a/cli/tools/bench/mod.rs +++ b/cli/tools/bench/mod.rs @@ -15,11 +15,11 @@ use crate::tools::test::format_test_error; use crate::tools::test::TestFilter; use crate::util::file_watcher; use crate::util::fs::collect_specifiers; +use crate::util::fs::WalkEntry; use crate::util::path::is_script_ext; use crate::version::get_user_agent; use crate::worker::CliMainWorkerFactory; -use deno_config::glob::FilePatterns; use deno_config::glob::PathGlobMatch; use deno_config::glob::PathOrPattern; use deno_core::error::generic_error; @@ -396,22 +396,23 @@ async fn bench_specifiers( } /// Checks if the path has a basename and extension Deno supports for benches. -fn is_supported_bench_path(path: &Path, patterns: &FilePatterns) -> bool { - if !is_script_ext(path) { +fn is_supported_bench_path(entry: WalkEntry) -> bool { + if !is_script_ext(entry.path) { false - } else if has_supported_bench_path_name(path) { + } else if has_supported_bench_path_name(entry.path) { true } else { // allow someone to explicitly specify a path - let matches_exact_path_or_pattern = patterns + let matches_exact_path_or_pattern = entry + .patterns .include .as_ref() .map(|p| { p.inner().iter().any(|p| match p { - PathOrPattern::Path(p) => p == path, + PathOrPattern::Path(p) => p == entry.path, PathOrPattern::RemoteUrl(_) => false, PathOrPattern::Pattern(p) => { - p.matches_path(path) == PathGlobMatch::Matched + p.matches_path(entry.path) == PathGlobMatch::Matched } }) }) diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 5cc705741cc1ab..380f07ba4d152f 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -400,11 +400,8 @@ fn collect_coverages( ) .context("Invalid ignore pattern.")?, }; - let file_paths = FileCollector::new(|file_path, _| { - file_path - .extension() - .map(|ext| ext == "json") - .unwrap_or(false) + let file_paths = FileCollector::new(|e| { + e.path.extension().map(|ext| ext == "json").unwrap_or(false) }) .ignore_git_folder() .ignore_node_modules() diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index 0b7b26e3143636..f173782dc43ff9 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -102,7 +102,7 @@ pub async fn doc(flags: Flags, doc_flags: DocFlags) -> Result<(), AnyError> { )?), exclude: Default::default(), }, - |_, _| true, + |_| true, )?; let graph = module_graph_creator .create_graph(GraphKind::TypesOnly, module_specifiers.clone()) diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 86fc9700eea88b..0f6afb232b750e 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -154,7 +154,7 @@ async fn format_files( } fn collect_fmt_files(files: FilePatterns) -> Result, AnyError> { - FileCollector::new(|path, _| is_supported_ext_fmt(path)) + FileCollector::new(|e| is_supported_ext_fmt(e.path)) .ignore_git_folder() .ignore_node_modules() .ignore_vendor_folder() diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index 1240b391f89abc..0e032dc2db52c6 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -261,7 +261,7 @@ async fn lint_files( } fn collect_lint_files(files: FilePatterns) -> Result, AnyError> { - FileCollector::new(|path, _| is_script_ext(path)) + FileCollector::new(|e| is_script_ext(e.path)) .ignore_git_folder() .ignore_node_modules() .ignore_vendor_folder() diff --git a/cli/tools/registry/tar.rs b/cli/tools/registry/tar.rs index b5c5980d35903d..f33b151f630ec6 100644 --- a/cli/tools/registry/tar.rs +++ b/cli/tools/registry/tar.rs @@ -2,6 +2,7 @@ use bytes::Bytes; use deno_ast::MediaType; +use deno_ast::ModuleSpecifier; use deno_config::glob::FilePatterns; use deno_core::anyhow::Context; use deno_core::error::AnyError; @@ -48,8 +49,21 @@ pub fn create_gzipped_tarball( let mut tar = TarGzArchive::new(); let mut files = vec![]; - let iter_paths = FileCollector::new(|path, _| { - path.file_name().map(|s| s != ".DS_Store").unwrap_or(true) + let iter_paths = FileCollector::new(|e| { + if !e.file_type.is_file() { + if let Some(specifier) = ModuleSpecifier::from_file_path(e.path).ok() { + diagnostics_collector.push(PublishDiagnostic::UnsupportedFileType { + specifier, + kind: if e.file_type.is_symlink() { + "symlink".to_owned() + } else { + format!("{:?}", e.file_type) + }, + }); + } + return false; + } + e.path.file_name().map(|s| s != ".DS_Store").unwrap_or(true) }) .ignore_git_folder() .ignore_node_modules() diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 1f7375dcd65077..7dda664c7c6657 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -16,6 +16,7 @@ use crate::module_loader::ModuleLoadPreparer; use crate::ops; use crate::util::file_watcher; use crate::util::fs::collect_specifiers; +use crate::util::fs::WalkEntry; use crate::util::path::get_extension; use crate::util::path::is_script_ext; use crate::util::path::mapped_specifier_for_tsc; @@ -1342,25 +1343,23 @@ pub async fn report_tests( (Ok(()), receiver) } -fn is_supported_test_path_predicate( - path: &Path, - patterns: &FilePatterns, -) -> bool { - if !is_script_ext(path) { +fn is_supported_test_path_predicate(entry: WalkEntry) -> bool { + if !is_script_ext(entry.path) { false - } else if has_supported_test_path_name(path) { + } else if has_supported_test_path_name(entry.path) { true } else { // allow someone to explicitly specify a path - let matches_exact_path_or_pattern = patterns + let matches_exact_path_or_pattern = entry + .patterns .include .as_ref() .map(|p| { p.inner().iter().any(|p| match p { - PathOrPattern::Path(p) => p == path, + PathOrPattern::Path(p) => p == entry.path, PathOrPattern::RemoteUrl(_) => false, PathOrPattern::Pattern(p) => { - p.matches_path(path) == PathGlobMatch::Matched + p.matches_path(entry.path) == PathGlobMatch::Matched } }) }) @@ -1426,7 +1425,7 @@ fn collect_specifiers_with_test_mode( collect_specifiers(files.clone(), is_supported_test_path_predicate)?; if *include_inline { - return collect_specifiers(files, |p, _| is_supported_test_ext(p)).map( + return collect_specifiers(files, |e| is_supported_test_ext(e.path)).map( |specifiers| { specifiers .into_iter() @@ -1602,8 +1601,8 @@ pub async fn run_tests_with_watch( let module_graph_creator = factory.module_graph_creator().await?; let file_fetcher = factory.file_fetcher()?; let test_modules = if test_options.doc { - collect_specifiers(test_options.files.clone(), |p, _| { - is_supported_test_ext(p) + collect_specifiers(test_options.files.clone(), |e| { + is_supported_test_ext(e.path) }) } else { collect_specifiers( diff --git a/cli/util/fs.rs b/cli/util/fs.rs index 41c41683bf382b..005481f9a4f189 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use std::collections::HashSet; use std::env::current_dir; use std::fmt::Write as FmtWrite; +use std::fs::FileType; use std::fs::OpenOptions; use std::io::Error; use std::io::ErrorKind; @@ -318,9 +319,16 @@ impl GitIgnoreTree { } } +#[derive(Debug, Clone)] +pub struct WalkEntry<'a> { + pub path: &'a Path, + pub file_type: &'a FileType, + pub patterns: &'a FilePatterns, +} + /// 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 struct FileCollector bool> { +pub struct FileCollector bool> { file_filter: TFilter, ignore_git_folder: bool, ignore_node_modules: bool, @@ -328,7 +336,7 @@ pub struct FileCollector bool> { use_gitignore: bool, } -impl bool> FileCollector { +impl bool> FileCollector { pub fn new(file_filter: TFilter) -> Self { Self { file_filter, @@ -442,8 +450,11 @@ impl bool> FileCollector { if should_ignore_dir { iterator.skip_current_dir(); } - } else if (self.file_filter)(&c, &file_patterns) - && visited_paths.insert(c.clone()) + } else if (self.file_filter)(WalkEntry { + path: &c, + file_type: &file_type, + patterns: &file_patterns, + }) && visited_paths.insert(c.clone()) { target_files.push(c); } @@ -458,7 +469,7 @@ impl bool> FileCollector { /// Note: This ignores all .git and node_modules folders. pub fn collect_specifiers( mut files: FilePatterns, - predicate: impl Fn(&Path, &FilePatterns) -> bool, + predicate: impl Fn(WalkEntry) -> bool, ) -> Result, AnyError> { let mut prepared = vec![]; @@ -931,9 +942,9 @@ mod tests { ignore_dir_path.to_path_buf(), )]), }; - let file_collector = FileCollector::new(|path, _| { + let file_collector = FileCollector::new(|e| { // exclude dotfiles - path + e.path .file_name() .and_then(|f| f.to_str()) .map(|f| !f.starts_with('.')) @@ -1055,9 +1066,9 @@ mod tests { let ignore_dir_files = ["g.d.ts", ".gitignore"]; create_files(&ignore_dir_path, &ignore_dir_files); - let predicate = |path: &Path, _: &FilePatterns| { + let predicate = |e: WalkEntry| { // exclude dotfiles - path + e.path .file_name() .and_then(|f| f.to_str()) .map(|f| !f.starts_with('.')) diff --git a/tests/integration/publish_tests.rs b/tests/integration/publish_tests.rs index ec5345553452b4..521b13a266435a 100644 --- a/tests/integration/publish_tests.rs +++ b/tests/integration/publish_tests.rs @@ -414,7 +414,7 @@ fn includes_directories() { } #[test] -fn includes_dotenv() { +fn not_includes_gitignored_dotenv() { let context = publish_context_builder().build(); let temp_dir = context.temp_dir().path(); temp_dir.join("deno.json").write_json(&json!({ @@ -425,6 +425,7 @@ fn includes_dotenv() { temp_dir.join("main.ts").write(""); temp_dir.join(".env").write("FOO=BAR"); + temp_dir.join(".gitignore").write(".env"); let output = context .new_command() From 746de4e38f7e92a36022bdf3d5c4beb80417d4be Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 6 Mar 2024 16:01:10 -0500 Subject: [PATCH 03/10] Improve gitignore code --- cli/util/fs.rs | 105 ++++++++++++++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 41 deletions(-) diff --git a/cli/util/fs.rs b/cli/util/fs.rs index 005481f9a4f189..caa52f25c7f75e 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use std::cell::RefCell; use std::collections::HashMap; use std::collections::HashSet; use std::env::current_dir; @@ -276,47 +275,65 @@ impl DirGitIgnores { struct GitIgnoreTree { fs: Arc, - // todo(THIS PR): I think refcell can be removed - ignores: RefCell>>>, + ignores: HashMap>>, } impl GitIgnoreTree { pub fn get_resolved_git_ignore( - &self, + &mut self, + dir_path: &Path, + ) -> Option> { + self.get_resolved_git_ignore_inner(dir_path, None) + } + + fn get_resolved_git_ignore_inner( + &mut self, dir_path: &Path, + maybe_parent: Option<&Path>, ) -> Option> { - let maybe_resolved = self.ignores.borrow().get(dir_path).cloned(); + let maybe_resolved = self.ignores.get(dir_path).cloned(); if let Some(resolved) = maybe_resolved { resolved } else { - // todo(THIS PR): stop searching when the root .git folder is found - let parent = dir_path - .parent() - .and_then(|parent| self.get_resolved_git_ignore(parent)); - let current = self - .fs - .read_text_file_sync(&dir_path.join(".gitignore")) - .ok() - .and_then(|text| { - let mut builder = ignore::gitignore::GitignoreBuilder::new(dir_path); - for line in text.lines() { - builder.add_line(None, line).ok()?; - } - let gitignore = builder.build().ok()?; - Some(Rc::new(gitignore)) - }); - let resolved = if parent.is_none() && current.is_none() { - None - } else { - Some(Rc::new(DirGitIgnores { current, parent })) - }; - self - .ignores - .borrow_mut() - .insert(dir_path.to_owned(), resolved.clone()); + let resolved = self.resolve_gitignore_in_dir(dir_path, maybe_parent); + self.ignores.insert(dir_path.to_owned(), resolved.clone()); resolved } } + + fn resolve_gitignore_in_dir( + &mut self, + dir_path: &Path, + maybe_parent: Option<&Path>, + ) -> Option> { + if let Some(parent) = maybe_parent { + // stop searching if the parent dir had a .git directory in it + if self.fs.exists_sync(&parent.join(".git")) { + return None; + } + } + + let parent = dir_path.parent().and_then(|parent| { + self.get_resolved_git_ignore_inner(parent, Some(dir_path)) + }); + let current = self + .fs + .read_text_file_sync(&dir_path.join(".gitignore")) + .ok() + .and_then(|text| { + let mut builder = ignore::gitignore::GitignoreBuilder::new(dir_path); + for line in text.lines() { + builder.add_line(None, line).ok()?; + } + let gitignore = builder.build().ok()?; + Some(Rc::new(gitignore)) + }); + if parent.is_none() && current.is_none() { + None + } else { + Some(Rc::new(DirGitIgnores { current, parent })) + } + } } #[derive(Debug, Clone)] @@ -372,8 +389,8 @@ impl bool> FileCollector { file_patterns: FilePatterns, ) -> Result, AnyError> { fn is_pattern_matched( - maybe_git_ignores: &Option, - path: &PathBuf, + maybe_git_ignore: Option<&DirGitIgnores>, + path: &Path, is_dir: bool, file_patterns: &FilePatterns, ) -> bool { @@ -386,13 +403,9 @@ impl bool> FileCollector { match file_patterns.matches_path_detail(path, path_kind) { FilePatternsMatch::Passed => { // check gitignore - let is_gitignored = maybe_git_ignores + let is_gitignored = maybe_git_ignore .as_ref() - .and_then(|git_ignores| { - git_ignores - .get_resolved_git_ignore(path) - .map(|resolved| resolved.is_ignored(path, is_dir)) - }) + .map(|git_ignore| git_ignore.is_ignored(path, is_dir)) .unwrap_or(false); !is_gitignored } @@ -401,10 +414,10 @@ impl bool> FileCollector { } } - let maybe_git_ignores = if self.use_gitignore { + let mut maybe_git_ignores = if self.use_gitignore { Some(GitIgnoreTree { fs: Arc::new(deno_runtime::deno_fs::RealFs), - ignores: RefCell::new(HashMap::new()), + ignores: HashMap::new(), }) } else { None @@ -427,7 +440,17 @@ impl bool> FileCollector { let file_type = e.file_type(); let is_dir = file_type.is_dir(); let c = e.path().to_path_buf(); - if !is_pattern_matched(&maybe_git_ignores, &c, is_dir, &file_patterns) { + let maybe_gitignore = + maybe_git_ignores.as_mut().and_then(|git_ignores| { + let dir_path = if is_dir { &c } else { c.parent()? }; + git_ignores.get_resolved_git_ignore(dir_path) + }); + if !is_pattern_matched( + maybe_gitignore.as_deref(), + &c, + is_dir, + &file_patterns, + ) { if is_dir { iterator.skip_current_dir(); } From 186c513c5e9dde62496b98079303bff635af4e64 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 6 Mar 2024 18:26:33 -0500 Subject: [PATCH 04/10] Add in-memory fs, more tests, and upgrade deno_config --- Cargo.lock | 4 +- Cargo.toml | 3 - cli/Cargo.toml | 2 +- cli/util/fs.rs | 98 +------ cli/util/gitignore.rs | 151 ++++++++++ cli/util/mod.rs | 1 + ext/fs/in_memory_fs.rs | 424 +++++++++++++++++++++++++++++ ext/fs/lib.rs | 2 + tests/integration/bench_tests.rs | 31 +++ tests/integration/fmt_tests.rs | 27 ++ tests/integration/lint_tests.rs | 25 ++ tests/integration/publish_tests.rs | 49 ++-- tests/integration/test_tests.rs | 30 ++ 13 files changed, 731 insertions(+), 116 deletions(-) create mode 100644 cli/util/gitignore.rs create mode 100644 ext/fs/in_memory_fs.rs diff --git a/Cargo.lock b/Cargo.lock index 4e31f26e28cf99..0cc57ec9e87b74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1232,7 +1232,9 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.12.0" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "551c3ec08515dba219aad02bb65b8de96ec33922978f4af0181f921681af9060" dependencies = [ "anyhow", "glob", diff --git a/Cargo.toml b/Cargo.toml index 04e6f119470517..b2f6f4fa3c5b87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -359,6 +359,3 @@ opt-level = 3 opt-level = 3 [profile.release.package.base64-simd] opt-level = 3 - -[patch.crates-io] -deno_config = { path = "../deno_config" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 5656c77f85ad8b..c86f1cbebc10ad 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -64,7 +64,7 @@ winres.workspace = true [dependencies] deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] } deno_cache_dir = { workspace = true } -deno_config = "=0.12.0" +deno_config = "=0.13.0" deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "=0.113.1", features = ["html"] } deno_emit = "=0.38.2" diff --git a/cli/util/fs.rs b/cli/util/fs.rs index caa52f25c7f75e..c4dc863c8206ea 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use std::collections::HashMap; use std::collections::HashSet; use std::env::current_dir; use std::fmt::Write as FmtWrite; @@ -11,7 +10,6 @@ use std::io::ErrorKind; use std::io::Write; use std::path::Path; use std::path::PathBuf; -use std::rc::Rc; use std::sync::Arc; use std::time::Duration; use walkdir::WalkDir; @@ -29,6 +27,8 @@ use deno_runtime::deno_crypto::rand; use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_node::PathClean; +use crate::util::gitignore::DirGitIgnores; +use crate::util::gitignore::GitIgnoreTree; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::util::progress_bar::ProgressMessagePrompt; @@ -247,95 +247,6 @@ pub fn resolve_from_cwd(path: &Path) -> Result { Ok(normalize_path(resolved_path)) } -struct DirGitIgnores { - current: Option>, - parent: Option>, -} - -impl DirGitIgnores { - pub fn is_ignored(&self, path: &Path, is_dir: bool) -> bool { - let mut is_ignored = false; - if let Some(parent) = &self.parent { - is_ignored = parent.is_ignored(path, is_dir); - } - if let Some(current) = &self.current { - match current.matched(path, is_dir) { - ignore::Match::None => {} - ignore::Match::Ignore(_) => { - is_ignored = true; - } - ignore::Match::Whitelist(_) => { - is_ignored = false; - } - } - } - is_ignored - } -} - -struct GitIgnoreTree { - fs: Arc, - ignores: HashMap>>, -} - -impl GitIgnoreTree { - pub fn get_resolved_git_ignore( - &mut self, - dir_path: &Path, - ) -> Option> { - self.get_resolved_git_ignore_inner(dir_path, None) - } - - fn get_resolved_git_ignore_inner( - &mut self, - dir_path: &Path, - maybe_parent: Option<&Path>, - ) -> Option> { - let maybe_resolved = self.ignores.get(dir_path).cloned(); - if let Some(resolved) = maybe_resolved { - resolved - } else { - let resolved = self.resolve_gitignore_in_dir(dir_path, maybe_parent); - self.ignores.insert(dir_path.to_owned(), resolved.clone()); - resolved - } - } - - fn resolve_gitignore_in_dir( - &mut self, - dir_path: &Path, - maybe_parent: Option<&Path>, - ) -> Option> { - if let Some(parent) = maybe_parent { - // stop searching if the parent dir had a .git directory in it - if self.fs.exists_sync(&parent.join(".git")) { - return None; - } - } - - let parent = dir_path.parent().and_then(|parent| { - self.get_resolved_git_ignore_inner(parent, Some(dir_path)) - }); - let current = self - .fs - .read_text_file_sync(&dir_path.join(".gitignore")) - .ok() - .and_then(|text| { - let mut builder = ignore::gitignore::GitignoreBuilder::new(dir_path); - for line in text.lines() { - builder.add_line(None, line).ok()?; - } - let gitignore = builder.build().ok()?; - Some(Rc::new(gitignore)) - }); - if parent.is_none() && current.is_none() { - None - } else { - Some(Rc::new(DirGitIgnores { current, parent })) - } - } -} - #[derive(Debug, Clone)] pub struct WalkEntry<'a> { pub path: &'a Path, @@ -415,10 +326,7 @@ impl bool> FileCollector { } let mut maybe_git_ignores = if self.use_gitignore { - Some(GitIgnoreTree { - fs: Arc::new(deno_runtime::deno_fs::RealFs), - ignores: HashMap::new(), - }) + Some(GitIgnoreTree::new(Arc::new(deno_runtime::deno_fs::RealFs))) } else { None }; diff --git a/cli/util/gitignore.rs b/cli/util/gitignore.rs new file mode 100644 index 00000000000000..da9065494ce430 --- /dev/null +++ b/cli/util/gitignore.rs @@ -0,0 +1,151 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use std::collections::HashMap; +use std::path::Path; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; + +/// Resolved gitignore for a directory. +pub struct DirGitIgnores { + current: Option>, + parent: Option>, +} + +impl DirGitIgnores { + pub fn is_ignored(&self, path: &Path, is_dir: bool) -> bool { + let mut is_ignored = false; + if let Some(parent) = &self.parent { + is_ignored = parent.is_ignored(path, is_dir); + } + if let Some(current) = &self.current { + match current.matched(path, is_dir) { + ignore::Match::None => {} + ignore::Match::Ignore(_) => { + is_ignored = true; + } + ignore::Match::Whitelist(_) => { + is_ignored = false; + } + } + } + is_ignored + } +} + +/// Resolves gitignores in a directory tree taking into account +/// ancestor gitignores that may be found in a directory. +pub struct GitIgnoreTree { + fs: Arc, + ignores: HashMap>>, +} + +impl GitIgnoreTree { + pub fn new(fs: Arc) -> Self { + Self { + fs, + ignores: Default::default(), + } + } + + pub fn get_resolved_git_ignore( + &mut self, + dir_path: &Path, + ) -> Option> { + self.get_resolved_git_ignore_inner(dir_path, None) + } + + fn get_resolved_git_ignore_inner( + &mut self, + dir_path: &Path, + maybe_parent: Option<&Path>, + ) -> Option> { + let maybe_resolved = self.ignores.get(dir_path).cloned(); + if let Some(resolved) = maybe_resolved { + resolved + } else { + let resolved = self.resolve_gitignore_in_dir(dir_path, maybe_parent); + self.ignores.insert(dir_path.to_owned(), resolved.clone()); + resolved + } + } + + fn resolve_gitignore_in_dir( + &mut self, + dir_path: &Path, + maybe_parent: Option<&Path>, + ) -> Option> { + if let Some(parent) = maybe_parent { + // stop searching if the parent dir had a .git directory in it + if self.fs.exists_sync(&parent.join(".git")) { + return None; + } + } + + let parent = dir_path.parent().and_then(|parent| { + self.get_resolved_git_ignore_inner(parent, Some(dir_path)) + }); + let current = self + .fs + .read_text_file_sync(&dir_path.join(".gitignore")) + .ok() + .and_then(|text| { + let mut builder = ignore::gitignore::GitignoreBuilder::new(dir_path); + for line in text.lines() { + builder.add_line(None, line).ok()?; + } + let gitignore = builder.build().ok()?; + Some(Rc::new(gitignore)) + }); + if parent.is_none() && current.is_none() { + None + } else { + Some(Rc::new(DirGitIgnores { current, parent })) + } + } +} + +#[cfg(test)] +mod test { + use deno_runtime::deno_fs::InMemoryFs; + + use super::*; + + #[test] + fn git_ignore_tree() { + let fs = InMemoryFs::default(); + fs.setup_text_files(vec![ + ("/.gitignore".into(), "file.txt".into()), + ("/sub_dir/.gitignore".into(), "data.txt".into()), + ( + "/sub_dir/sub_dir/.gitignore".into(), + "!file.txt\nignore.txt".into(), + ), + ]); + let mut ignore_tree = GitIgnoreTree::new(Arc::new(fs)); + let mut run_test = |path: &str, expected: bool| { + let path = PathBuf::from(path); + let gitignore = ignore_tree + .get_resolved_git_ignore(path.parent().unwrap()) + .unwrap(); + assert_eq!( + gitignore.is_ignored(&path, /* is_dir */ false), + expected, + "Path: {}", + path.display() + ); + }; + run_test("/file.txt", true); + run_test("/other.txt", false); + run_test("/data.txt", false); + run_test("/sub_dir/file.txt", true); + run_test("/sub_dir/other.txt", false); + run_test("/sub_dir/data.txt", true); + run_test("/sub_dir/sub_dir/file.txt", false); // unignored up here + run_test("/sub_dir/sub_dir/sub_dir/file.txt", false); + run_test("/sub_dir/sub_dir/sub_dir/ignore.txt", true); + run_test("/sub_dir/sub_dir/ignore.txt", true); + run_test("/sub_dir/ignore.txt", false); + run_test("/ignore.txt", false); + } +} diff --git a/cli/util/mod.rs b/cli/util/mod.rs index a6f72bc048775f..7e0e1bd3700d51 100644 --- a/cli/util/mod.rs +++ b/cli/util/mod.rs @@ -8,6 +8,7 @@ pub mod display; pub mod draw_thread; pub mod file_watcher; pub mod fs; +pub mod gitignore; pub mod logger; pub mod path; pub mod progress_bar; diff --git a/ext/fs/in_memory_fs.rs b/ext/fs/in_memory_fs.rs new file mode 100644 index 00000000000000..e353717cef062a --- /dev/null +++ b/ext/fs/in_memory_fs.rs @@ -0,0 +1,424 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::io::Error; +use std::io::ErrorKind; +use std::path::Path; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; + +use deno_core::normalize_path; +use deno_core::parking_lot::Mutex; +use deno_io::fs::File; +use deno_io::fs::FsError; +use deno_io::fs::FsResult; +use deno_io::fs::FsStat; + +use crate::interface::FsDirEntry; +use crate::interface::FsFileType; +use crate::FileSystem; +use crate::OpenOptions; + +#[derive(Debug)] +enum PathEntry { + Dir, + File(Vec), +} + +/// A very basic in-memory file system useful for swapping out in +/// the place of a RealFs for testing purposes. +/// +/// Please develop this out as you need functionality. +#[derive(Debug, Default)] +pub struct InMemoryFs { + entries: Mutex>>, +} + +impl InMemoryFs { + pub fn setup_text_files(&self, files: Vec<(String, String)>) { + for (path, text) in files { + let path = PathBuf::from(path); + self.mkdir_sync(path.parent().unwrap(), true, 0).unwrap(); + self + .write_file_sync( + &path, + OpenOptions::write(true, false, false, None), + &text.into_bytes(), + ) + .unwrap(); + } + } + + fn get_entry(&self, path: &Path) -> Option> { + let path = normalize_path(path); + self.entries.lock().get(&path).cloned() + } +} + +#[async_trait::async_trait(?Send)] +impl FileSystem for InMemoryFs { + fn cwd(&self) -> FsResult { + Err(FsError::NotSupported) + } + + fn tmp_dir(&self) -> FsResult { + Err(FsError::NotSupported) + } + + fn chdir(&self, _path: &Path) -> FsResult<()> { + Err(FsError::NotSupported) + } + + fn umask(&self, _mask: Option) -> FsResult { + Err(FsError::NotSupported) + } + + fn open_sync( + &self, + _path: &Path, + _options: OpenOptions, + ) -> FsResult> { + Err(FsError::NotSupported) + } + async fn open_async( + &self, + path: PathBuf, + options: OpenOptions, + ) -> FsResult> { + self.open_sync(&path, options) + } + + fn mkdir_sync( + &self, + path: &Path, + recursive: bool, + _mode: u32, + ) -> FsResult<()> { + let path = normalize_path(path); + + if let Some(parent) = path.parent() { + let entry = self.entries.lock().get(parent).cloned(); + match entry { + Some(entry) => match &*entry { + PathEntry::File(_) => { + return Err(FsError::Io(Error::new( + ErrorKind::InvalidInput, + "Parent is a file", + ))) + } + PathEntry::Dir => {} + }, + None => { + if recursive { + self.mkdir_sync(parent, true, 0)?; + } else { + return Err(FsError::Io(Error::new( + ErrorKind::NotFound, + "Not found", + ))); + } + } + } + } + + let entry = self.entries.lock().get(&path).cloned(); + match entry { + Some(entry) => match &*entry { + PathEntry::File(_) => Err(FsError::Io(Error::new( + ErrorKind::InvalidInput, + "Is a file", + ))), + PathEntry::Dir => Ok(()), + }, + None => { + self.entries.lock().insert(path, Arc::new(PathEntry::Dir)); + Ok(()) + } + } + } + async fn mkdir_async( + &self, + path: PathBuf, + recursive: bool, + mode: u32, + ) -> FsResult<()> { + self.mkdir_sync(&path, recursive, mode) + } + + fn chmod_sync(&self, _path: &Path, _mode: u32) -> FsResult<()> { + Err(FsError::NotSupported) + } + async fn chmod_async(&self, path: PathBuf, mode: u32) -> FsResult<()> { + self.chmod_sync(&path, mode) + } + + fn chown_sync( + &self, + _path: &Path, + _uid: Option, + _gid: Option, + ) -> FsResult<()> { + Err(FsError::NotSupported) + } + async fn chown_async( + &self, + path: PathBuf, + uid: Option, + gid: Option, + ) -> FsResult<()> { + self.chown_sync(&path, uid, gid) + } + + fn remove_sync(&self, _path: &Path, _recursive: bool) -> FsResult<()> { + Err(FsError::NotSupported) + } + async fn remove_async(&self, path: PathBuf, recursive: bool) -> FsResult<()> { + self.remove_sync(&path, recursive) + } + + fn copy_file_sync(&self, _from: &Path, _to: &Path) -> FsResult<()> { + Err(FsError::NotSupported) + } + async fn copy_file_async(&self, from: PathBuf, to: PathBuf) -> FsResult<()> { + self.copy_file_sync(&from, &to) + } + + fn cp_sync(&self, _from: &Path, _to: &Path) -> FsResult<()> { + Err(FsError::NotSupported) + } + async fn cp_async(&self, from: PathBuf, to: PathBuf) -> FsResult<()> { + self.cp_sync(&from, &to) + } + + fn stat_sync(&self, path: &Path) -> FsResult { + let entry = self.get_entry(path); + match entry { + Some(entry) => match &*entry { + PathEntry::Dir => Ok(FsStat { + is_file: false, + is_directory: true, + is_symlink: false, + size: 0, + mtime: None, + atime: None, + birthtime: None, + dev: 0, + ino: 0, + mode: 0, + nlink: 0, + uid: 0, + gid: 0, + rdev: 0, + blksize: 0, + blocks: 0, + is_block_device: false, + is_char_device: false, + is_fifo: false, + is_socket: false, + }), + PathEntry::File(data) => Ok(FsStat { + is_file: true, + is_directory: false, + is_symlink: false, + size: data.len() as u64, + mtime: None, + atime: None, + birthtime: None, + dev: 0, + ino: 0, + mode: 0, + nlink: 0, + uid: 0, + gid: 0, + rdev: 0, + blksize: 0, + blocks: 0, + is_block_device: false, + is_char_device: false, + is_fifo: false, + is_socket: false, + }), + }, + None => { + return Err(FsError::Io(Error::new(ErrorKind::NotFound, "Not found"))) + } + } + } + async fn stat_async(&self, path: PathBuf) -> FsResult { + self.stat_sync(&path) + } + + fn lstat_sync(&self, _path: &Path) -> FsResult { + Err(FsError::NotSupported) + } + async fn lstat_async(&self, path: PathBuf) -> FsResult { + self.lstat_sync(&path) + } + + fn realpath_sync(&self, _path: &Path) -> FsResult { + Err(FsError::NotSupported) + } + async fn realpath_async(&self, path: PathBuf) -> FsResult { + self.realpath_sync(&path) + } + + fn read_dir_sync(&self, _path: &Path) -> FsResult> { + Err(FsError::NotSupported) + } + async fn read_dir_async(&self, path: PathBuf) -> FsResult> { + self.read_dir_sync(&path) + } + + fn rename_sync(&self, _oldpath: &Path, _newpath: &Path) -> FsResult<()> { + Err(FsError::NotSupported) + } + async fn rename_async( + &self, + oldpath: PathBuf, + newpath: PathBuf, + ) -> FsResult<()> { + self.rename_sync(&oldpath, &newpath) + } + + fn link_sync(&self, _oldpath: &Path, _newpath: &Path) -> FsResult<()> { + Err(FsError::NotSupported) + } + async fn link_async( + &self, + oldpath: PathBuf, + newpath: PathBuf, + ) -> FsResult<()> { + self.link_sync(&oldpath, &newpath) + } + + fn symlink_sync( + &self, + _oldpath: &Path, + _newpath: &Path, + _file_type: Option, + ) -> FsResult<()> { + Err(FsError::NotSupported) + } + async fn symlink_async( + &self, + oldpath: PathBuf, + newpath: PathBuf, + file_type: Option, + ) -> FsResult<()> { + self.symlink_sync(&oldpath, &newpath, file_type) + } + + fn read_link_sync(&self, _path: &Path) -> FsResult { + Err(FsError::NotSupported) + } + async fn read_link_async(&self, path: PathBuf) -> FsResult { + self.read_link_sync(&path) + } + + fn truncate_sync(&self, _path: &Path, _len: u64) -> FsResult<()> { + Err(FsError::NotSupported) + } + async fn truncate_async(&self, path: PathBuf, len: u64) -> FsResult<()> { + self.truncate_sync(&path, len) + } + + fn utime_sync( + &self, + _path: &Path, + _atime_secs: i64, + _atime_nanos: u32, + _mtime_secs: i64, + _mtime_nanos: u32, + ) -> FsResult<()> { + Err(FsError::NotSupported) + } + async fn utime_async( + &self, + path: PathBuf, + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()> { + self.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) + } + + fn write_file_sync( + &self, + path: &Path, + options: OpenOptions, + data: &[u8], + ) -> FsResult<()> { + let path = normalize_path(path); + let has_parent_dir = path + .parent() + .and_then(|parent| self.get_entry(parent)) + .map(|e| matches!(*e, PathEntry::Dir)) + .unwrap_or(false); + if !has_parent_dir { + return Err(FsError::Io(Error::new( + ErrorKind::NotFound, + "Parent directory does not exist", + ))); + } + let mut entries = self.entries.lock(); + let entry = entries.entry(path.clone()); + match entry { + Entry::Occupied(mut entry) => { + if let PathEntry::File(existing_data) = &**entry.get() { + if options.create_new { + return Err(FsError::Io(Error::new( + ErrorKind::AlreadyExists, + "File already exists", + ))); + } + if options.append { + let mut new_data = existing_data.clone(); + new_data.extend_from_slice(data); + entry.insert(Arc::new(PathEntry::File(new_data))); + } else { + entry.insert(Arc::new(PathEntry::File(data.to_vec()))); + } + Ok(()) + } else { + Err(FsError::Io(Error::new( + ErrorKind::InvalidInput, + "Not a file", + ))) + } + } + Entry::Vacant(entry) => { + entry.insert(Arc::new(PathEntry::File(data.to_vec()))); + Ok(()) + } + } + } + + async fn write_file_async( + &self, + path: PathBuf, + options: OpenOptions, + data: Vec, + ) -> FsResult<()> { + self.write_file_sync(&path, options, &data) + } + + fn read_file_sync(&self, path: &Path) -> FsResult> { + let entry = self.get_entry(path); + match entry { + Some(entry) => match &*entry { + PathEntry::File(data) => Ok(data.clone()), + PathEntry::Dir => Err(FsError::Io(Error::new( + ErrorKind::InvalidInput, + "Is a directory", + ))), + }, + None => Err(FsError::Io(Error::new(ErrorKind::NotFound, "Not found"))), + } + } + async fn read_file_async(&self, path: PathBuf) -> FsResult> { + self.read_file_sync(&path) + } +} diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs index c31cdd85dd6858..05b119e2e1d9f3 100644 --- a/ext/fs/lib.rs +++ b/ext/fs/lib.rs @@ -1,10 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +mod in_memory_fs; mod interface; mod ops; mod std_fs; pub mod sync; +pub use crate::in_memory_fs::InMemoryFs; pub use crate::interface::FileSystem; pub use crate::interface::FileSystemRc; pub use crate::interface::FsDirEntry; diff --git a/tests/integration/bench_tests.rs b/tests/integration/bench_tests.rs index 8621679dc20470..e0d3f87242f4a3 100644 --- a/tests/integration/bench_tests.rs +++ b/tests/integration/bench_tests.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use deno_core::serde_json::json; use deno_core::url::Url; use test_util as util; use test_util::itest; @@ -8,6 +9,7 @@ use util::assert_contains; use util::assert_not_contains; use util::env_vars_for_npm_tests; use util::TestContext; +use util::TestContextBuilder; itest!(overloads { args: "bench bench/overloads.ts", @@ -285,3 +287,32 @@ fn conditionally_loads_type_graph() { .run(); assert_not_contains!(output.combined_output(), "type_reference.d.ts"); } + +#[test] +fn opt_out_top_level_exclude_via_bench_unexclude() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let temp_dir = context.temp_dir().path(); + temp_dir.join("deno.json").write_json(&json!({ + "bench": { + "exclude": [ "!excluded.bench.ts" ] + }, + "exclude": [ "excluded.bench.ts", "actually_excluded.bench.ts" ] + })); + + temp_dir + .join("main.bench.ts") + .write("Deno.bench('test1', () => {});"); + temp_dir + .join("excluded.bench.ts") + .write("Deno.bench('test2', () => {});"); + temp_dir + .join("actually_excluded.bench.ts") + .write("Deno.bench('test3', () => {});"); + + let output = context.new_command().arg("bench").run(); + output.assert_exit_code(0); + let output = output.combined_output(); + assert_contains!(output, "main.bench.ts"); + assert_contains!(output, "excluded.bench.ts"); + assert_not_contains!(output, "actually_excluded.bench.ts"); +} diff --git a/tests/integration/fmt_tests.rs b/tests/integration/fmt_tests.rs index 6588ae10abd3a5..4174548887c4ec 100644 --- a/tests/integration/fmt_tests.rs +++ b/tests/integration/fmt_tests.rs @@ -1,8 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use deno_core::serde_json::json; use test_util as util; use test_util::itest; use util::assert_contains; +use util::assert_not_contains; use util::PathRef; use util::TestContext; use util::TestContextBuilder; @@ -351,3 +353,28 @@ fn fmt_with_glob_config_and_flags() { assert_contains!(output, "Found 2 not formatted files in 2 files"); } + +#[test] +fn opt_out_top_level_exclude_via_fmt_unexclude() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let temp_dir = context.temp_dir().path(); + temp_dir.join("deno.json").write_json(&json!({ + "fmt": { + "exclude": [ "!excluded.ts" ] + }, + "exclude": [ "excluded.ts", "actually_excluded.ts" ] + })); + + temp_dir.join("main.ts").write("const a = 1;"); + temp_dir.join("excluded.ts").write("const a = 2;"); + temp_dir + .join("actually_excluded.ts") + .write("const a = 2;"); + + let output = context.new_command().arg("fmt").run(); + output.assert_exit_code(0); + let output = output.combined_output(); + assert_contains!(output, "main.ts"); + assert_contains!(output, "excluded.ts"); + assert_not_contains!(output, "actually_excluded.ts"); +} diff --git a/tests/integration/lint_tests.rs b/tests/integration/lint_tests.rs index ae04142625553a..a55fb1ef4b2315 100644 --- a/tests/integration/lint_tests.rs +++ b/tests/integration/lint_tests.rs @@ -1,6 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use deno_core::serde_json::json; use test_util::assert_contains; +use test_util::assert_not_contains; use test_util::itest; use test_util::TestContextBuilder; @@ -252,3 +254,26 @@ itest!(no_slow_types_workspace { cwd: Some("lint/no_slow_types_workspace"), exit_code: 1, }); + +#[test] +fn opt_out_top_level_exclude_via_lint_unexclude() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let temp_dir = context.temp_dir().path(); + temp_dir.join("deno.json").write_json(&json!({ + "lint": { + "exclude": [ "!excluded.ts" ] + }, + "exclude": [ "excluded.ts", "actually_excluded.ts" ] + })); + + temp_dir.join("main.ts").write("const a = 1;"); + temp_dir.join("excluded.ts").write("const a = 2;"); + temp_dir.join("actually_excluded.ts").write("const a = 2;"); + + let output = context.new_command().arg("lint").run(); + output.assert_exit_code(1); + let output = output.combined_output(); + assert_contains!(output, "main.ts"); + assert_contains!(output, "excluded.ts"); + assert_not_contains!(output, "actually_excluded.ts"); +} diff --git a/tests/integration/publish_tests.rs b/tests/integration/publish_tests.rs index 521b13a266435a..1f081a375ecfb9 100644 --- a/tests/integration/publish_tests.rs +++ b/tests/integration/publish_tests.rs @@ -357,7 +357,7 @@ fn ignores_directories() { } #[test] -fn includes_directories_with_gitignore() { +fn not_include_gitignored_file_even_if_matched_in_include() { let context = publish_context_builder().build(); let temp_dir = context.temp_dir().path(); temp_dir.join("deno.json").write_json(&json!({ @@ -365,26 +365,49 @@ fn includes_directories_with_gitignore() { "version": "1.0.0", "exports": "./main.ts", "publish": { - "include": [ "deno.json", "main.ts" ] + // won't match ignored because it needs to be + // unexcluded via a negated glob in exclude + "include": [ "deno.json", "*.ts" ] } })); - temp_dir.join(".gitignore").write("main.ts"); + temp_dir.join(".gitignore").write("ignored.ts"); temp_dir.join("main.ts").write(""); temp_dir.join("ignored.ts").write(""); - let output = context - .new_command() - .arg("publish") - .arg("--token") - .arg("sadfasdf") - .run(); + let output = context.new_command().arg("publish").arg("--dry-run").run(); output.assert_exit_code(0); let output = output.combined_output(); assert_contains!(output, "main.ts"); + // it's gitignored assert_not_contains!(output, "ignored.ts"); } +#[test] +fn includes_directories_with_gitignore_when_unexcluded() { + let context = publish_context_builder().build(); + let temp_dir = context.temp_dir().path(); + temp_dir.join("deno.json").write_json(&json!({ + "name": "@foo/bar", + "version": "1.0.0", + "exports": "./main.ts", + "publish": { + "include": [ "deno.json", "*.ts" ], + "exclude": [ "!ignored.ts" ] + } + })); + + temp_dir.join(".gitignore").write("ignored.ts"); + temp_dir.join("main.ts").write(""); + temp_dir.join("ignored.ts").write(""); + + let output = context.new_command().arg("publish").arg("--dry-run").run(); + output.assert_exit_code(0); + let output = output.combined_output(); + assert_contains!(output, "main.ts"); + assert_contains!(output, "ignored.ts"); +} + #[test] fn includes_directories() { let context = publish_context_builder().build(); @@ -427,13 +450,7 @@ fn not_includes_gitignored_dotenv() { temp_dir.join(".env").write("FOO=BAR"); temp_dir.join(".gitignore").write(".env"); - let output = context - .new_command() - .arg("publish") - .arg("--token") - .arg("sadfasdf") - .arg("--dry-run") - .run(); + let output = context.new_command().arg("publish").arg("--dry-run").run(); output.assert_exit_code(0); let output = output.combined_output(); assert_contains!(output, "main.ts"); diff --git a/tests/integration/test_tests.rs b/tests/integration/test_tests.rs index cd85fd102d1e0d..d5768b5bad850d 100644 --- a/tests/integration/test_tests.rs +++ b/tests/integration/test_tests.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use deno_core::serde_json::json; use deno_core::url::Url; use test_util as util; use test_util::itest; @@ -668,3 +669,32 @@ itest!(test_include_relative_pattern_dot_slash { output: "test/relative_pattern_dot_slash/output.out", cwd: Some("test/relative_pattern_dot_slash"), }); + +#[test] +fn opt_out_top_level_exclude_via_test_unexclude() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let temp_dir = context.temp_dir().path(); + temp_dir.join("deno.json").write_json(&json!({ + "test": { + "exclude": [ "!excluded.test.ts" ] + }, + "exclude": [ "excluded.test.ts", "actually_excluded.test.ts" ] + })); + + temp_dir + .join("main.test.ts") + .write("Deno.test('test1', () => {});"); + temp_dir + .join("excluded.test.ts") + .write("Deno.test('test2', () => {});"); + temp_dir + .join("actually_excluded.test.ts") + .write("Deno.test('test3', () => {});"); + + let output = context.new_command().arg("test").run(); + output.assert_exit_code(0); + let output = output.combined_output(); + assert_contains!(output, "main.test.ts"); + assert_contains!(output, "excluded.test.ts"); + assert_not_contains!(output, "actually_excluded.test.ts"); +} From 7409da98802c4e123014eed46fd805c9b1122b68 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 6 Mar 2024 18:31:00 -0500 Subject: [PATCH 05/10] Lint --- cli/lsp/config.rs | 4 ++-- cli/tools/registry/tar.rs | 2 +- ext/fs/in_memory_fs.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 2d8f83d9c16e27..b31b3d25c2907e 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1083,7 +1083,7 @@ impl Config { pub fn get_disabled_paths(&self) -> PathOrPatternSet { let mut path_or_patterns = vec![]; if let Some(cf) = self.maybe_config_file() { - if let Some(files) = cf.to_files_config().ok() { + if let Ok(files) = cf.to_files_config() { for path in files.exclude.into_path_or_patterns() { path_or_patterns.push(path); } @@ -1177,7 +1177,7 @@ fn specifier_enabled( workspace_folders: &[(Url, lsp::WorkspaceFolder)], ) -> bool { if let Some(cf) = config_file { - if let Some(files) = cf.to_files_config().ok() { + if let Ok(files) = cf.to_files_config() { if !files.matches_specifier(specifier) { return false; } diff --git a/cli/tools/registry/tar.rs b/cli/tools/registry/tar.rs index f33b151f630ec6..ef96b95d19c8c5 100644 --- a/cli/tools/registry/tar.rs +++ b/cli/tools/registry/tar.rs @@ -51,7 +51,7 @@ pub fn create_gzipped_tarball( let iter_paths = FileCollector::new(|e| { if !e.file_type.is_file() { - if let Some(specifier) = ModuleSpecifier::from_file_path(e.path).ok() { + if let Ok(specifier) = ModuleSpecifier::from_file_path(e.path) { diagnostics_collector.push(PublishDiagnostic::UnsupportedFileType { specifier, kind: if e.file_type.is_symlink() { diff --git a/ext/fs/in_memory_fs.rs b/ext/fs/in_memory_fs.rs index e353717cef062a..5c7ebec2d066c2 100644 --- a/ext/fs/in_memory_fs.rs +++ b/ext/fs/in_memory_fs.rs @@ -1,5 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +#![allow(clippy::disallowed_types)] + use std::collections::hash_map::Entry; use std::collections::HashMap; use std::io::Error; @@ -241,9 +243,7 @@ impl FileSystem for InMemoryFs { is_socket: false, }), }, - None => { - return Err(FsError::Io(Error::new(ErrorKind::NotFound, "Not found"))) - } + None => Err(FsError::Io(Error::new(ErrorKind::NotFound, "Not found"))), } } async fn stat_async(&self, path: PathBuf) -> FsResult { From 8c5bd3e9d41cdcdf65b928d9691155e2b366ca3f Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 6 Mar 2024 20:30:01 -0500 Subject: [PATCH 06/10] Latest changes --- Cargo.lock | 2 -- Cargo.toml | 3 +++ cli/lsp/documents.rs | 5 +++-- cli/tools/bench/mod.rs | 23 +++++------------------ cli/tools/test/mod.rs | 23 +++++------------------ cli/util/fs.rs | 4 ++++ cli/util/path.rs | 35 +++++++++++++++++++++++++++++++++++ 7 files changed, 55 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0cc57ec9e87b74..1123902e87c4ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1233,8 +1233,6 @@ dependencies = [ [[package]] name = "deno_config" version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551c3ec08515dba219aad02bb65b8de96ec33922978f4af0181f921681af9060" dependencies = [ "anyhow", "glob", diff --git a/Cargo.toml b/Cargo.toml index b2f6f4fa3c5b87..04e6f119470517 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -359,3 +359,6 @@ opt-level = 3 opt-level = 3 [profile.release.package.base64-simd] opt-level = 3 + +[patch.crates-io] +deno_config = { path = "../deno_config" } diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index c4a11beff422ac..a6960c24fa42fe 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -1341,8 +1341,9 @@ impl Documents { .inner() .iter() .map(|p| match p { - PathOrPattern::Path(p) => { - Cow::Owned(p.to_string_lossy().to_string()) + PathOrPattern::Path(p) => p.to_string_lossy(), + PathOrPattern::NegatedPath(p) => { + Cow::Owned(format!("!{}", p.to_string_lossy())) } PathOrPattern::RemoteUrl(p) => Cow::Borrowed(p.as_str()), PathOrPattern::Pattern(p) => p.as_str(), diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs index 345a9f903b7c12..bbba0a03106a43 100644 --- a/cli/tools/bench/mod.rs +++ b/cli/tools/bench/mod.rs @@ -17,11 +17,10 @@ use crate::util::file_watcher; use crate::util::fs::collect_specifiers; use crate::util::fs::WalkEntry; use crate::util::path::is_script_ext; +use crate::util::path::matches_pattern_or_exact_path; use crate::version::get_user_agent; use crate::worker::CliMainWorkerFactory; -use deno_config::glob::PathGlobMatch; -use deno_config::glob::PathOrPattern; use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::error::JsError; @@ -401,23 +400,11 @@ fn is_supported_bench_path(entry: WalkEntry) -> bool { false } else if has_supported_bench_path_name(entry.path) { true - } else { + } else if let Some(include) = &entry.patterns.include { // allow someone to explicitly specify a path - let matches_exact_path_or_pattern = entry - .patterns - .include - .as_ref() - .map(|p| { - p.inner().iter().any(|p| match p { - PathOrPattern::Path(p) => p == entry.path, - PathOrPattern::RemoteUrl(_) => false, - PathOrPattern::Pattern(p) => { - p.matches_path(entry.path) == PathGlobMatch::Matched - } - }) - }) - .unwrap_or(false); - matches_exact_path_or_pattern + matches_pattern_or_exact_path(include, entry.path) + } else { + false } } diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 7dda664c7c6657..2a996e0d00f882 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -20,14 +20,13 @@ use crate::util::fs::WalkEntry; use crate::util::path::get_extension; use crate::util::path::is_script_ext; use crate::util::path::mapped_specifier_for_tsc; +use crate::util::path::matches_pattern_or_exact_path; use crate::worker::CliMainWorkerFactory; use deno_ast::swc::common::comments::CommentKind; use deno_ast::MediaType; use deno_ast::SourceRangedForSpanned; use deno_config::glob::FilePatterns; -use deno_config::glob::PathGlobMatch; -use deno_config::glob::PathOrPattern; use deno_core::anyhow; use deno_core::anyhow::bail; use deno_core::anyhow::Context as _; @@ -1348,23 +1347,11 @@ fn is_supported_test_path_predicate(entry: WalkEntry) -> bool { false } else if has_supported_test_path_name(entry.path) { true - } else { + } else if let Some(include) = &entry.patterns.include { // allow someone to explicitly specify a path - let matches_exact_path_or_pattern = entry - .patterns - .include - .as_ref() - .map(|p| { - p.inner().iter().any(|p| match p { - PathOrPattern::Path(p) => p == entry.path, - PathOrPattern::RemoteUrl(_) => false, - PathOrPattern::Pattern(p) => { - p.matches_path(entry.path) == PathGlobMatch::Matched - } - }) - }) - .unwrap_or(false); - matches_exact_path_or_pattern + matches_pattern_or_exact_path(include, entry.path) + } else { + false } } diff --git a/cli/util/fs.rs b/cli/util/fs.rs index c4dc863c8206ea..0bcad3d91cc2a4 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -419,6 +419,10 @@ pub fn collect_specifiers( prepared.push(url); } } + PathOrPattern::NegatedPath(path) => { + // add it back + result.push(PathOrPattern::NegatedPath(path)); + } PathOrPattern::RemoteUrl(remote_url) => { prepared.push(remote_url); } diff --git a/cli/util/path.rs b/cli/util/path.rs index 496b37c5e83d1f..fed74cb0667fbf 100644 --- a/cli/util/path.rs +++ b/cli/util/path.rs @@ -6,6 +6,9 @@ use std::path::PathBuf; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; +use deno_config::glob::PathGlobMatch; +use deno_config::glob::PathOrPattern; +use deno_config::glob::PathOrPatternSet; use deno_core::error::uri_error; use deno_core::error::AnyError; @@ -244,6 +247,38 @@ pub fn root_url_to_safe_local_dirname(root: &ModuleSpecifier) -> PathBuf { result } +/// Slightly different behaviour than the default matching +/// where an exact path needs to be matched to be opted-in +/// rather than just a partial directory match. +/// +/// This is used by the test and bench filtering. +pub fn matches_pattern_or_exact_path( + path_or_pattern_set: &PathOrPatternSet, + path: &Path, +) -> bool { + for p in path_or_pattern_set.inner().iter().rev() { + match p { + PathOrPattern::Path(p) => { + if p == path { + return true; + } + } + PathOrPattern::NegatedPath(p) => { + if path.starts_with(p) { + return false; + } + } + PathOrPattern::RemoteUrl(_) => {} + PathOrPattern::Pattern(p) => match p.matches_path(path) { + PathGlobMatch::Matched => return true, + PathGlobMatch::MatchedNegated => return false, + PathGlobMatch::NotMatched => {} + }, + } + } + false +} + #[cfg(test)] mod test { use super::*; From 826df373e63107961cf1010c51568cdea05b7f62 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 7 Mar 2024 12:37:45 -0500 Subject: [PATCH 07/10] Update deno_config --- Cargo.lock | 8 +++++--- Cargo.toml | 3 --- cli/Cargo.toml | 2 +- tests/integration/publish_tests.rs | 29 +++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fad3baf26c6fc8..ea78a8405ab9a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1232,7 +1232,9 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.13.0" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350ce390482f76434065c260dd4395f53b42becdf21fcca84fa5966117b7b659" dependencies = [ "anyhow", "glob", @@ -7274,7 +7276,7 @@ dependencies = [ "codespan-reporting", "log", "naga", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "profiling", "raw-window-handle", "ron", @@ -7315,7 +7317,7 @@ dependencies = [ "naga", "objc", "once_cell", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "profiling", "range-alloc", "raw-window-handle", diff --git a/Cargo.toml b/Cargo.toml index f2ccdae73048ca..e3eaf97a1c801d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -359,6 +359,3 @@ opt-level = 3 opt-level = 3 [profile.release.package.base64-simd] opt-level = 3 - -[patch.crates-io] -deno_config = { path = "../deno_config" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index c86f1cbebc10ad..bbea41b548372a 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -64,7 +64,7 @@ winres.workspace = true [dependencies] deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] } deno_cache_dir = { workspace = true } -deno_config = "=0.13.0" +deno_config = "=0.14.0" deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "=0.113.1", features = ["html"] } deno_emit = "=0.38.2" diff --git a/tests/integration/publish_tests.rs b/tests/integration/publish_tests.rs index 7a931543b4303c..7815086aeaa358 100644 --- a/tests/integration/publish_tests.rs +++ b/tests/integration/publish_tests.rs @@ -442,6 +442,35 @@ fn includes_directories_with_gitignore_when_unexcluded() { assert_contains!(output, "ignored.ts"); } +#[test] +fn includes_unexcluded_sub_dir() { + let context = publish_context_builder().build(); + let temp_dir = context.temp_dir().path(); + temp_dir.join("deno.json").write_json(&json!({ + "name": "@foo/bar", + "version": "1.0.0", + "exports": "./included1.ts", + "publish": { + "exclude": [ + "ignored", + "!ignored/unexcluded", + ] + } + })); + + temp_dir.join("included1.ts").write(""); + temp_dir.join("ignored/unexcluded").create_dir_all(); + temp_dir.join("ignored/ignored.ts").write(""); + temp_dir.join("ignored/unexcluded/included2.ts").write(""); + + let output = context.new_command().arg("publish").arg("--dry-run").run(); + output.assert_exit_code(0); + let output = output.combined_output(); + assert_contains!(output, "included1.ts"); + assert_contains!(output, "included2.ts"); + assert_not_contains!(output, "ignored.ts"); +} + #[test] fn includes_directories() { let context = publish_context_builder().build(); From 2f626ba467392c05c33cf5e527d4d43d96f8933a Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 7 Mar 2024 12:42:01 -0500 Subject: [PATCH 08/10] Remove out of date comment --- cli/tools/registry/tar.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cli/tools/registry/tar.rs b/cli/tools/registry/tar.rs index ef96b95d19c8c5..0da4107640f610 100644 --- a/cli/tools/registry/tar.rs +++ b/cli/tools/registry/tar.rs @@ -157,16 +157,6 @@ pub fn create_gzipped_tarball( .with_context(|| { format!("Unable to add file to tarball '{}'", path.display()) })?; - - // todo: handle surfacing this - // diagnostics_collector.push(PublishDiagnostic::UnsupportedFileType { - // specifier, - // kind: if file_type.is_symlink() { - // "symlink".to_owned() - // } else { - // format!("{file_type:?}") - // }, - // }); } let v = tar.finish().context("Unable to finish tarball")?; From 2270674ef5870a9fe403a671df29357370304afc Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 7 Mar 2024 16:47:30 -0500 Subject: [PATCH 09/10] Better errors for shadowed negations --- Cargo.lock | 4 ++-- cli/Cargo.toml | 2 +- cli/args/mod.rs | 10 +++++----- cli/lsp/config.rs | 10 +++++++++- cli/lsp/documents.rs | 4 ++-- cli/tools/coverage/mod.rs | 4 ++-- cli/tools/doc.rs | 10 ++++++---- cli/util/fs.rs | 2 +- tests/integration/lsp_tests.rs | 4 +--- 9 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae5558cef3396a..7d003e2b6f4305 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1232,9 +1232,9 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350ce390482f76434065c260dd4395f53b42becdf21fcca84fa5966117b7b659" +checksum = "61c801e30b12aa3f15f59d4d4947621eef34d6798a93f6a5037c0efa26f87a8b" dependencies = [ "anyhow", "glob", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index d2f36b8b9dec46..11fdcc1230a1c3 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -64,7 +64,7 @@ winres.workspace = true [dependencies] deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] } deno_cache_dir = { workspace = true } -deno_config = "=0.14.0" +deno_config = "=0.14.1" deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "=0.113.1", features = ["html"] } deno_emit = "=0.38.2" diff --git a/cli/args/mod.rs b/cli/args/mod.rs index c396c90db95340..d72b419476742d 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -1750,14 +1750,14 @@ fn resolve_files( if let Some(file_flags) = maybe_file_flags { if !file_flags.include.is_empty() { maybe_files_config.include = - Some(PathOrPatternSet::from_relative_path_or_patterns( + Some(PathOrPatternSet::from_include_relative_path_or_patterns( initial_cwd, &file_flags.include, )?); } if !file_flags.ignore.is_empty() { maybe_files_config.exclude = - PathOrPatternSet::from_relative_path_or_patterns( + PathOrPatternSet::from_exclude_relative_path_or_patterns( initial_cwd, &file_flags.ignore, )?; @@ -1886,7 +1886,7 @@ mod test { temp_dir.write("pages/[id].ts", ""); let temp_dir_path = temp_dir.path().as_path(); - let error = PathOrPatternSet::from_relative_path_or_patterns( + let error = PathOrPatternSet::from_include_relative_path_or_patterns( temp_dir_path, &["data/**********.ts".to_string()], ) @@ -1897,7 +1897,7 @@ mod test { Some(FilePatterns { base: temp_dir_path.to_path_buf(), include: Some( - PathOrPatternSet::from_relative_path_or_patterns( + PathOrPatternSet::from_include_relative_path_or_patterns( temp_dir_path, &[ "data/test1.?s".to_string(), @@ -1908,7 +1908,7 @@ mod test { ) .unwrap(), ), - exclude: PathOrPatternSet::from_relative_path_or_patterns( + exclude: PathOrPatternSet::from_exclude_relative_path_or_patterns( temp_dir_path, &["nested/**/*bazz.ts".to_string()], ) diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index b31b3d25c2907e..ca63948b20e4a1 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1095,7 +1095,15 @@ impl Config { continue; }; let settings = self.workspace_settings_for_specifier(workspace_uri); - if settings.enable.unwrap_or_else(|| self.has_config_file()) { + let is_enabled = settings.enable.unwrap_or_else(|| { + self.has_config_file() + || settings + .enable_paths + .as_ref() + .map(|p| !p.is_empty()) + .unwrap_or(false) + }); + if is_enabled { for path in &settings.disable_paths { path_or_patterns.push(PathOrPattern::Path(workspace_path.join(path))); } diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index a6960c24fa42fe..7912dad78b6a85 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -2360,7 +2360,7 @@ console.log(b, "hello deno"); file_patterns: FilePatterns { base: temp_dir.path().to_path_buf(), include: Some( - PathOrPatternSet::from_relative_path_or_patterns( + PathOrPatternSet::from_include_relative_path_or_patterns( temp_dir.path().as_path(), &[ "root1".to_string(), @@ -2421,7 +2421,7 @@ console.log(b, "hello deno"); file_patterns: FilePatterns { base: temp_dir.path().to_path_buf(), include: Default::default(), - exclude: PathOrPatternSet::from_relative_path_or_patterns( + exclude: PathOrPatternSet::from_exclude_relative_path_or_patterns( temp_dir.path().as_path(), &[ "root1".to_string(), diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 380f07ba4d152f..66c0923de4317d 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -388,13 +388,13 @@ fn collect_coverages( initial_cwd.to_path_buf(), )]) } else { - PathOrPatternSet::from_relative_path_or_patterns( + PathOrPatternSet::from_include_relative_path_or_patterns( initial_cwd, &files.include, )? } }), - exclude: PathOrPatternSet::from_relative_path_or_patterns( + exclude: PathOrPatternSet::from_exclude_relative_path_or_patterns( initial_cwd, &files.ignore, ) diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index f173782dc43ff9..013a407aa0128b 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -96,10 +96,12 @@ pub async fn doc(flags: Flags, doc_flags: DocFlags) -> Result<(), AnyError> { let module_specifiers = collect_specifiers( FilePatterns { base: cli_options.initial_cwd().to_path_buf(), - include: Some(PathOrPatternSet::from_relative_path_or_patterns( - cli_options.initial_cwd(), - source_files, - )?), + include: Some( + PathOrPatternSet::from_include_relative_path_or_patterns( + cli_options.initial_cwd(), + source_files, + )?, + ), exclude: Default::default(), }, |_| true, diff --git a/cli/util/fs.rs b/cli/util/fs.rs index 0bcad3d91cc2a4..d765d371e351c0 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -1014,7 +1014,7 @@ mod tests { FilePatterns { base: root_dir_path.to_path_buf(), include: Some( - PathOrPatternSet::from_relative_path_or_patterns( + PathOrPatternSet::from_include_relative_path_or_patterns( root_dir_path.as_path(), &[ "http://localhost:8080".to_string(), diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 8165cc86a26e87..a7193ff59e0cff 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -1652,11 +1652,9 @@ fn lsp_workspace_disable_enable_paths() { .unwrap() }, name: "project".to_string(), - }]) - .set_deno_enable(false); + }]); }, json!({ "deno": { - "enable": false, "disablePaths": ["./worker/node.ts"], "enablePaths": ["./worker"], } }), From 7c96fdef8643f71ba4d7a37385f907483ab9681d Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 7 Mar 2024 18:16:15 -0500 Subject: [PATCH 10/10] Review updates. --- cli/lsp/config.rs | 15 +++++++-------- cli/util/fs.rs | 18 +++++++++--------- ext/fs/in_memory_fs.rs | 1 + 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index ca63948b20e4a1..120828a798b5e1 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1095,14 +1095,13 @@ impl Config { continue; }; let settings = self.workspace_settings_for_specifier(workspace_uri); - let is_enabled = settings.enable.unwrap_or_else(|| { - self.has_config_file() - || settings - .enable_paths - .as_ref() - .map(|p| !p.is_empty()) - .unwrap_or(false) - }); + let is_enabled = settings + .enable_paths + .as_ref() + .map(|p| !p.is_empty()) + .unwrap_or_else(|| { + settings.enable.unwrap_or_else(|| self.has_config_file()) + }); if is_enabled { for path in &settings.disable_paths { path_or_patterns.push(PathOrPattern::Path(workspace_path.join(path))); diff --git a/cli/util/fs.rs b/cli/util/fs.rs index d765d371e351c0..f6354097a9eb95 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -347,15 +347,15 @@ impl bool> FileCollector { }; let file_type = e.file_type(); let is_dir = file_type.is_dir(); - let c = e.path().to_path_buf(); + let path = e.path().to_path_buf(); let maybe_gitignore = maybe_git_ignores.as_mut().and_then(|git_ignores| { - let dir_path = if is_dir { &c } else { c.parent()? }; + let dir_path = if is_dir { &path } else { path.parent()? }; git_ignores.get_resolved_git_ignore(dir_path) }); if !is_pattern_matched( maybe_gitignore.as_deref(), - &c, + &path, is_dir, &file_patterns, ) { @@ -363,7 +363,7 @@ impl bool> FileCollector { iterator.skip_current_dir(); } } else if is_dir { - let should_ignore_dir = c + let should_ignore_dir = path .file_name() .map(|dir_name| { let dir_name = dir_name.to_string_lossy().to_lowercase(); @@ -374,20 +374,20 @@ impl bool> FileCollector { _ => false, }; // allow the user to opt out of ignoring by explicitly specifying the dir - file != c && is_ignored_file + file != path && is_ignored_file }) .unwrap_or(false) - || !visited_paths.insert(c.clone()); + || !visited_paths.insert(path.clone()); if should_ignore_dir { iterator.skip_current_dir(); } } else if (self.file_filter)(WalkEntry { - path: &c, + path: &path, file_type: &file_type, patterns: &file_patterns, - }) && visited_paths.insert(c.clone()) + }) && visited_paths.insert(path.clone()) { - target_files.push(c); + target_files.push(path); } } } diff --git a/ext/fs/in_memory_fs.rs b/ext/fs/in_memory_fs.rs index 5c7ebec2d066c2..fdd0ad7e734f98 100644 --- a/ext/fs/in_memory_fs.rs +++ b/ext/fs/in_memory_fs.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Allow using Arc for this module. #![allow(clippy::disallowed_types)] use std::collections::hash_map::Entry;