refactor(cli): move lint command and options into modules

This commit is contained in:
Boshen 2023-03-18 16:03:32 +08:00
parent 0ec5f0b111
commit 5135118bc8
No known key found for this signature in database
GPG key ID: 6AC90C77AAAA6ABC
8 changed files with 387 additions and 427 deletions

View file

@ -1,172 +0,0 @@
use clap::{builder::ValueParser, Arg, ArgAction, Command};
#[must_use]
pub fn command() -> Command {
Command::new("oxc")
.bin_name("oxc")
.version("alpha")
.author("Boshen")
.about("The JavaScript Oxidation Compiler")
.subcommand_required(true)
.arg_required_else_help(true)
.subcommand(lint_subcommand())
}
fn lint_subcommand() -> Command {
Command::new("lint")
.alias("check")
.about("Lint this repository.")
.arg_required_else_help(true)
.arg(
Arg::new("fix")
.long("fix")
.required(false)
.action(ArgAction::SetTrue)
.help("This option allows you to enable oxc to fix as many issues as possible. If enabled, only unfixed issues are reported in the output")
)
.arg(
Arg::new("quiet")
.long("quiet")
.required(false)
.action(ArgAction::SetTrue)
.help("This option allows you to disable reporting on warnings. If you enable this option, only errors are reported by oxc_lint.")
)
.arg(
Arg::new("ignore-path")
.long("ignore-path")
.required(false)
.help("This option allows you to specify the file to use as your .eslintignore.")
)
.arg(
Arg::new("no-ignore")
.long("no-ignore")
.required(false)
.action(ArgAction::SetTrue)
.help("Disables excluding of files from .eslintignore files, --ignore-path flags, --ignore-pattern flags.")
)
.arg(
Arg::new("ignore-pattern")
.long("ignore-pattern")
.required(false)
.action(ArgAction::Append)
.help("This option allows you to specify patterns of files to ignore (in addition to those in .eslintignore).")
)
.arg(
Arg::new("max-warnings")
.long("max-warnings")
.value_parser(clap::value_parser!(usize))
.required(false)
.help("This option allows you to specify a warning threshold, which can be used to force oxc_lint to exit with an error status if there are too many warning-level rule violations in your project.")
)
.arg(
Arg::new("path")
.value_name("PATH")
.num_args(1..)
.required(true)
.help("File or Directory paths to scan. Directories are scanned recursively.")
.value_parser(ValueParser::path_buf()),
)
}
#[cfg(test)]
mod test {
use std::path::PathBuf;
use clap::ArgMatches;
use super::command;
#[test]
fn verify_command() {
command().debug_assert();
}
fn get_lint_matches(arg: &str) -> ArgMatches {
let matches = command().try_get_matches_from(arg.split(' ')).unwrap();
let matches = matches.subcommand_matches("lint");
assert!(matches.is_some());
matches.unwrap().clone()
}
#[test]
fn test_lint_default() {
let matches = get_lint_matches("oxc lint .");
assert_eq!(matches.get_one::<PathBuf>("path"), Some(&PathBuf::from(".")));
assert_eq!(matches.get_flag("fix"), false);
assert_eq!(matches.get_flag("quiet"), false);
assert_eq!(matches.get_one::<String>("ignore-path"), None);
assert_eq!(matches.get_flag("no-ignore"), false);
assert_eq!(matches.get_one::<String>("ignore-pattern"), None);
assert_eq!(matches.get_one::<usize>("max-warnings"), None);
}
#[test]
fn test_lint_path() {
let matches = get_lint_matches("oxc lint .");
assert_eq!(matches.get_one::<PathBuf>("path"), Some(&PathBuf::from(".")));
}
#[test]
fn test_lint_multiple_paths() {
let matches = get_lint_matches("oxc lint foo bar baz");
assert_eq!(
matches.get_many::<PathBuf>("path").unwrap().collect::<Vec<_>>(),
[&PathBuf::from("foo"), &PathBuf::from("bar"), &PathBuf::from("baz")]
);
}
#[test]
fn test_lint_check_path() {
let matches = get_lint_matches("oxc check /path/to/dir");
assert_eq!(matches.get_one::<PathBuf>("path"), Some(&PathBuf::from("/path/to/dir")));
}
#[test]
fn test_lint_quiet_true() {
let matches = get_lint_matches("oxc lint foo.js --quiet");
assert!(matches.get_flag("quiet"));
}
#[test]
fn test_lint_fix_true() {
let matches = get_lint_matches("oxc lint foo.js --fix");
assert!(matches.get_flag("fix"));
}
#[test]
fn test_lint_max_warnings() {
let matches = get_lint_matches("oxc lint --max-warnings 10 foo.js");
assert_eq!(matches.get_one::<usize>("max-warnings"), Some(&10));
}
#[test]
fn test_lint_ignore_path() {
let matches = get_lint_matches("oxc lint --ignore-path .gitignore foo.js");
assert_eq!(matches.get_one::<String>("ignore-path"), Some(&".gitignore".to_string()));
}
#[test]
fn test_lint_no_ignore() {
let matches = get_lint_matches("oxc lint --no-ignore foo.js");
assert!(matches.get_flag("no-ignore"));
}
#[test]
fn test_lint_single_ignore_pattern() {
let matches = get_lint_matches("oxc lint --ignore-pattern \"./test\" foo.js");
assert_eq!(matches.get_one::<String>("ignore-pattern"), Some(&"\"./test\"".to_string()));
}
#[test]
fn test_lint_multiple_ignore_pattern() {
let matches = get_lint_matches(
"oxc lint --ignore-pattern \"./test\" --ignore-pattern \"bar.js\" foo.js",
);
let ignore_pattern = matches.get_many::<String>("ignore-pattern").unwrap();
let mut compare = vec![];
for pattern in ignore_pattern {
compare.push(pattern);
}
assert_eq!(compare, vec!["\"./test\"", "\"bar.js\""]);
}
}

View file

@ -1,180 +1,25 @@
mod command;
// mod git;
mod options;
mod lint;
mod result;
mod walk;
use std::{
fs,
io::{BufWriter, Write},
path::{Path, PathBuf},
rc::Rc,
sync::{mpsc, Arc},
use clap::Command;
use crate::lint::lint_command;
pub use crate::{
lint::{LintOptions, LintRunner},
result::CliRunResult,
walk::Walk,
};
use miette::NamedSource;
use oxc_allocator::Allocator;
use oxc_ast::SourceType;
use oxc_diagnostics::{Error, MinifiedFileError, Severity};
use oxc_linter::{Fixer, Linter};
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
pub use crate::{command::command, options::CliOptions, result::CliRunResult, walk::Walk};
pub struct Cli {
pub cli_options: CliOptions,
}
impl Cli {
#[must_use]
pub fn new(cli_options: CliOptions) -> Self {
Self { cli_options }
}
/// # Panics
///
/// * When `mpsc::channel` fails to send.
#[must_use]
pub fn lint(&self) -> CliRunResult {
let now = std::time::Instant::now();
let (tx_error, rx_error) = mpsc::channel::<(PathBuf, Vec<Error>)>();
let (tx_path, rx_path) = mpsc::channel::<Box<Path>>();
let mut number_of_files = 0;
rayon::join(
|| {
let paths = self
.cli_options
.paths
.iter()
.flat_map(|path| Walk::new(path, &self.cli_options).iter())
.filter(|path| {
if self.cli_options.no_ignore {
return true;
}
for pattern in &self.cli_options.ignore_pattern {
if pattern.matches_path(path) {
return false;
}
}
true
});
for path in paths {
number_of_files += 1;
tx_path.send(path).unwrap();
}
drop(tx_path);
},
move || {
let fix = self.cli_options.fix;
while let Ok(path) = rx_path.recv() {
let tx_error = tx_error.clone();
rayon::spawn(move || {
if let Some(diagnostics) = Self::lint_path(&path, fix) {
tx_error.send(diagnostics).unwrap();
}
drop(tx_error);
});
}
},
);
let mut buf_writer = BufWriter::new(std::io::stdout());
let mut number_of_warnings = 0;
let mut number_of_diagnostics = 0;
while let Ok((path, diagnostics)) = rx_error.recv() {
number_of_diagnostics += diagnostics.len();
for diagnostic in diagnostics {
if diagnostic.severity() == Some(Severity::Warning) {
number_of_warnings += 1;
// The --quiet flag follows ESLint's --quiet behavior as documented here: https://eslint.org/docs/latest/use/command-line-interface#--quiet
// Note that it does not disable ALL diagnostics, only Warning diagnostics
if self.cli_options.quiet {
continue;
}
if let Some(max_warnings) = self.cli_options.max_warnings {
if number_of_warnings > max_warnings {
continue;
}
}
}
let output = format!("{diagnostic:?}");
// Skip large output and print only once
if output.lines().any(|line| line.len() >= 400) {
let minified_diagnostic = Error::new(MinifiedFileError(path.clone()));
buf_writer.write_all(format!("{minified_diagnostic:?}").as_bytes()).unwrap();
break;
}
buf_writer.write_all(output.as_bytes()).unwrap();
}
}
buf_writer.flush().unwrap();
CliRunResult::LintResult {
duration: now.elapsed(),
number_of_files,
number_of_diagnostics,
number_of_warnings,
max_warnings_exceeded: self
.cli_options
.max_warnings
.map_or(false, |max_warnings| number_of_warnings > max_warnings),
}
}
fn lint_path(path: &Path, fix: bool) -> Option<(PathBuf, Vec<Error>)> {
let source_text = fs::read_to_string(path).unwrap_or_else(|_| panic!("{path:?} not found"));
let allocator = Allocator::default();
let source_type =
SourceType::from_path(path).unwrap_or_else(|_| panic!("incorrect {path:?}"));
let parser_source_text = source_text.clone();
let ret = Parser::new(&allocator, &parser_source_text, source_type).parse();
if !ret.errors.is_empty() {
return Some(Self::wrap_diagnostics(path, &source_text, ret.errors));
};
let program = allocator.alloc(ret.program);
let trivias = Rc::new(ret.trivias);
let semantic_ret = SemanticBuilder::new(source_type).build(program, &trivias);
if !semantic_ret.errors.is_empty() {
return Some(Self::wrap_diagnostics(path, &source_text, semantic_ret.errors));
};
let result = Linter::new().with_fix(fix).run(&Rc::new(semantic_ret.semantic), &source_text);
if result.is_empty() {
return None;
}
if fix {
let fix_result = Fixer::new(&source_text, result).fix();
fs::write(path, fix_result.fixed_code.as_bytes()).unwrap();
let errors = fix_result.messages.into_iter().map(|m| m.error).collect();
return Some(Self::wrap_diagnostics(path, &source_text, errors));
}
let errors = result.into_iter().map(|diagnostic| diagnostic.error).collect();
Some(Self::wrap_diagnostics(path, &source_text, errors))
}
fn wrap_diagnostics(
path: &Path,
source_text: &str,
diagnostics: Vec<Error>,
) -> (PathBuf, Vec<Error>) {
let source = Arc::new(NamedSource::new(path.to_string_lossy(), source_text.to_owned()));
let diagnostics = diagnostics
.into_iter()
.map(|diagnostic| diagnostic.with_source_code(Arc::clone(&source)))
.collect();
(path.to_path_buf(), diagnostics)
}
#[must_use]
pub fn command() -> Command {
Command::new("oxc")
.bin_name("oxc")
.version("alpha")
.author("Boshen")
.about("The JavaScript Oxidation Compiler")
.subcommand_required(true)
.arg_required_else_help(true)
.subcommand(lint_command())
}

View file

@ -0,0 +1,59 @@
use clap::{builder::ValueParser, Arg, ArgAction, Command};
pub fn lint_command() -> Command {
Command::new("lint")
.alias("check")
.about("Lint this repository.")
.arg_required_else_help(true)
.arg(
Arg::new("path")
.value_name("PATH")
.num_args(1..)
.required(true)
.value_parser(ValueParser::path_buf())
.help("File or Directory paths to scan. Directories are scanned recursively.")
)
.arg(
Arg::new("fix")
.long("fix")
.required(false)
.action(ArgAction::SetTrue)
.help("This option allows you to enable oxc to fix as many issues as possible. If enabled, only unfixed issues are reported in the output")
)
.arg(
Arg::new("quiet")
.long("quiet")
.required(false)
.action(ArgAction::SetTrue)
.help("This option allows you to disable reporting on warnings. If you enable this option, only errors are reported by oxc_lint.")
)
.arg(
Arg::new("ignore-path")
.long("ignore-path")
.required(false)
.value_parser(ValueParser::path_buf())
.help("This option allows you to specify the file to use as your .eslintignore.")
)
.arg(
Arg::new("no-ignore")
.long("no-ignore")
.required(false)
.action(ArgAction::SetTrue)
.help("Disables excluding of files from .eslintignore files, --ignore-path flags, --ignore-pattern flags.")
)
.arg(
Arg::new("ignore-pattern")
.long("ignore-pattern")
.required(false)
.action(ArgAction::Append)
.help("This option allows you to specify patterns of files to ignore (in addition to those in .eslintignore).")
)
.arg(
Arg::new("max-warnings")
.long("max-warnings")
.value_parser(clap::value_parser!(usize))
.default_value(None)
.required(false)
.help("This option allows you to specify a warning threshold, which can be used to force oxc_lint to exit with an error status if there are too many warning-level rule violations in your project.")
)
}

View file

@ -0,0 +1,120 @@
use std::path::PathBuf;
mod command;
mod runner;
use clap::ArgMatches;
pub use self::{command::lint_command, runner::LintRunner};
pub struct LintOptions {
pub paths: Vec<PathBuf>,
pub fix: bool,
pub quiet: bool,
pub ignore_path: PathBuf,
pub no_ignore: bool,
pub ignore_pattern: Vec<String>,
pub max_warnings: Option<usize>,
}
impl<'a> From<&'a ArgMatches> for LintOptions {
fn from(matches: &'a ArgMatches) -> Self {
Self {
paths: matches.get_many("path").map_or_else(
|| vec![PathBuf::from(".")],
|paths| paths.into_iter().cloned().collect(),
),
fix: matches.get_flag("fix"),
quiet: matches.get_flag("quiet"),
ignore_path: matches
.get_one::<PathBuf>("ignore-path")
.map_or_else(|| PathBuf::from(".eslintignore"), Clone::clone),
no_ignore: matches.get_flag("no-ignore"),
ignore_pattern: matches
.get_many::<String>("ignore-pattern")
.map(|patterns| patterns.into_iter().cloned().collect())
.unwrap_or_default(),
max_warnings: matches.get_one("max-warnings").copied(),
}
}
}
#[cfg(test)]
mod test {
use std::path::PathBuf;
use super::{lint_command, LintOptions};
#[test]
fn verify_command() {
lint_command().debug_assert();
}
fn get_lint_options(arg: &str) -> LintOptions {
let matches = lint_command().try_get_matches_from(arg.split(' ')).unwrap();
LintOptions::from(&matches)
}
#[test]
fn test_default() {
let options = get_lint_options("lint .");
assert_eq!(options.paths, vec![PathBuf::from(".")]);
assert!(!options.fix);
assert!(!options.quiet);
assert_eq!(options.ignore_path, PathBuf::from(".eslintignore"));
assert!(!options.no_ignore);
assert!(options.ignore_pattern.is_empty());
assert_eq!(options.max_warnings, None);
}
#[test]
fn test_multiple_paths() {
let options = get_lint_options("lint foo bar baz");
assert_eq!(
options.paths,
[PathBuf::from("foo"), PathBuf::from("bar"), PathBuf::from("baz")]
);
}
#[test]
fn test_quiet_true() {
let options = get_lint_options("lint foo.js --quiet");
assert!(options.quiet);
}
#[test]
fn test_fix_true() {
let options = get_lint_options("lint foo.js --fix");
assert!(options.fix);
}
#[test]
fn test_max_warnings() {
let options = get_lint_options("lint --max-warnings 10 foo.js");
assert_eq!(options.max_warnings, Some(10));
}
#[test]
fn test_ignore_path() {
let options = get_lint_options("lint --ignore-path .xxx foo.js");
assert_eq!(options.ignore_path, PathBuf::from(".xxx"));
}
#[test]
fn test_no_ignore() {
let options = get_lint_options("lint --no-ignore foo.js");
assert!(options.no_ignore);
}
#[test]
fn test_single_ignore_pattern() {
let options = get_lint_options("lint --ignore-pattern ./test foo.js");
assert_eq!(options.ignore_pattern, vec![String::from("./test")]);
}
#[test]
fn test_multiple_ignore_pattern() {
let options =
get_lint_options("lint --ignore-pattern ./test --ignore-pattern bar.js foo.js");
assert_eq!(options.ignore_pattern, vec![String::from("./test"), String::from("bar.js")]);
}
}

View file

@ -0,0 +1,178 @@
use std::{
fs,
io::{BufWriter, Write},
path::{Path, PathBuf},
rc::Rc,
sync::{mpsc, Arc},
};
use miette::NamedSource;
use oxc_allocator::Allocator;
use oxc_ast::SourceType;
use oxc_diagnostics::{Error, MinifiedFileError, Severity};
use oxc_linter::{Fixer, Linter};
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use super::LintOptions;
use crate::{CliRunResult, Walk};
pub struct LintRunner {
options: LintOptions,
}
impl LintRunner {
#[must_use]
pub fn new(options: LintOptions) -> Self {
Self { options }
}
/// # Panics
///
/// * When `mpsc::channel` fails to send.
#[must_use]
pub fn run(&self) -> CliRunResult {
let now = std::time::Instant::now();
let mut number_of_files = 0;
let mut number_of_warnings = 0;
let mut number_of_diagnostics = 0;
let (tx_error, rx_error) = mpsc::channel::<(PathBuf, Vec<Error>)>();
self.process_paths(&mut number_of_files, tx_error);
self.process_diagnostics(&mut number_of_warnings, &mut number_of_diagnostics, &rx_error);
CliRunResult::LintResult {
duration: now.elapsed(),
number_of_files,
number_of_diagnostics,
number_of_warnings,
max_warnings_exceeded: self
.options
.max_warnings
.map_or(false, |max_warnings| number_of_warnings > max_warnings),
}
}
fn process_paths(
&self,
number_of_files: &mut usize,
tx_error: mpsc::Sender<(PathBuf, Vec<Error>)>,
) {
let (tx_path, rx_path) = mpsc::channel::<Box<Path>>();
let options = &self.options;
let fix = options.fix;
rayon::join(
move || {
options.paths.iter().flat_map(|path| Walk::new(path, options).iter()).for_each(
|path| {
*number_of_files += 1;
tx_path.send(path).unwrap();
},
)
},
move || {
while let Ok(path) = rx_path.recv() {
let tx_error = tx_error.clone();
rayon::spawn(move || {
if let Some(diagnostics) = Self::lint_path(&path, fix) {
tx_error.send(diagnostics).unwrap();
}
drop(tx_error);
});
}
},
);
}
fn process_diagnostics(
&self,
number_of_warnings: &mut usize,
number_of_diagnostics: &mut usize,
rx_error: &mpsc::Receiver<(PathBuf, Vec<Error>)>,
) {
let mut buf_writer = BufWriter::new(std::io::stdout());
while let Ok((path, diagnostics)) = rx_error.recv() {
*number_of_diagnostics += diagnostics.len();
for diagnostic in diagnostics {
if diagnostic.severity() == Some(Severity::Warning) {
*number_of_warnings += 1;
// The --quiet flag follows ESLint's --quiet behavior as documented here: https://eslint.org/docs/latest/use/command-line-interface#--quiet
// Note that it does not disable ALL diagnostics, only Warning diagnostics
if self.options.quiet {
continue;
}
if let Some(max_warnings) = self.options.max_warnings {
if *number_of_warnings > max_warnings {
continue;
}
}
}
let output = format!("{diagnostic:?}");
// Skip large output and print only once
if output.lines().any(|line| line.len() >= 400) {
let minified_diagnostic = Error::new(MinifiedFileError(path.clone()));
buf_writer.write_all(format!("{minified_diagnostic:?}").as_bytes()).unwrap();
break;
}
buf_writer.write_all(output.as_bytes()).unwrap();
}
}
buf_writer.flush().unwrap();
}
fn lint_path(path: &Path, fix: bool) -> Option<(PathBuf, Vec<Error>)> {
let source_text = fs::read_to_string(path).unwrap_or_else(|_| panic!("{path:?} not found"));
let allocator = Allocator::default();
let source_type =
SourceType::from_path(path).unwrap_or_else(|_| panic!("incorrect {path:?}"));
let parser_source_text = source_text.clone();
let ret = Parser::new(&allocator, &parser_source_text, source_type).parse();
if !ret.errors.is_empty() {
return Some(Self::wrap_diagnostics(path, &source_text, ret.errors));
};
let program = allocator.alloc(ret.program);
let trivias = Rc::new(ret.trivias);
let semantic_ret = SemanticBuilder::new(source_type).build(program, &trivias);
if !semantic_ret.errors.is_empty() {
return Some(Self::wrap_diagnostics(path, &source_text, semantic_ret.errors));
};
let result = Linter::new().with_fix(fix).run(&Rc::new(semantic_ret.semantic), &source_text);
if result.is_empty() {
return None;
}
if fix {
let fix_result = Fixer::new(&source_text, result).fix();
fs::write(path, fix_result.fixed_code.as_bytes()).unwrap();
let errors = fix_result.messages.into_iter().map(|m| m.error).collect();
return Some(Self::wrap_diagnostics(path, &source_text, errors));
}
let errors = result.into_iter().map(|diagnostic| diagnostic.error).collect();
Some(Self::wrap_diagnostics(path, &source_text, errors))
}
fn wrap_diagnostics(
path: &Path,
source_text: &str,
diagnostics: Vec<Error>,
) -> (PathBuf, Vec<Error>) {
let source = Arc::new(NamedSource::new(path.to_string_lossy(), source_text.to_owned()));
let diagnostics = diagnostics
.into_iter()
.map(|diagnostic| diagnostic.with_source_code(Arc::clone(&source)))
.collect();
(path.to_path_buf(), diagnostics)
}
}

View file

@ -8,7 +8,7 @@ static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
use oxc_cli::{command, Cli, CliOptions, CliRunResult};
use oxc_cli::{command, CliRunResult, LintOptions, LintRunner};
use oxc_diagnostics::miette;
fn main() -> CliRunResult {
@ -17,20 +17,16 @@ fn main() -> CliRunResult {
miette::set_hook(Box::new(|_| Box::new(miette::MietteHandlerOpts::new().tab_width(4).build())))
.unwrap();
if let Some((subcommand, matches)) = command().get_matches().subcommand() {
let cli_options = CliOptions::try_from(matches);
if let Ok(cli_options) = cli_options {
// if cli_options.fix {
// Git::new().verify()?;
// }
let matches = command().get_matches();
let Some((subcommand, matches)) = matches.subcommand() else {
return CliRunResult::None
};
let cli = Cli::new(cli_options);
if subcommand == "lint" {
return cli.lint();
}
return CliRunResult::None;
match subcommand {
"lint" => {
let options = LintOptions::from(matches);
LintRunner::new(options).run()
}
_ => CliRunResult::None,
}
CliRunResult::None
}

View file

@ -1,66 +0,0 @@
use std::path::PathBuf;
use clap::ArgMatches;
use glob::Pattern;
pub struct CliOptions {
pub quiet: bool,
pub fix: bool,
pub max_warnings: Option<usize>,
pub paths: Vec<PathBuf>,
pub ignore_path: String,
pub no_ignore: bool,
pub ignore_pattern: Vec<Pattern>,
}
impl<'a> TryFrom<&'a ArgMatches> for CliOptions {
type Error = &'a str;
fn try_from(matches: &ArgMatches) -> Result<Self, Self::Error> {
let mut paths = vec![];
for path in matches.get_many::<PathBuf>("path").unwrap() {
let glob_result =
glob::glob(&path.to_string_lossy()).map_err(|_| "Failed to read glob pattern")?;
let globbed = glob_result
.map(|path_result| path_result.map_err(|_| "Failed to read path"))
.collect::<Result<Vec<PathBuf>, &str>>()?;
if globbed.is_empty() && path.canonicalize().is_err() {
return Err("Unable to find globbed files");
}
paths.extend(globbed);
}
let ignore_path = get_ignore_path(matches);
let no_ignore = matches.get_flag("no-ignore");
let ignore_pattern = get_ignore_pattern(matches);
Ok(Self {
quiet: matches.get_flag("quiet"),
fix: matches.get_flag("fix"),
max_warnings: matches.get_one("max-warnings").copied(),
paths,
ignore_path,
no_ignore,
ignore_pattern,
})
}
}
fn get_ignore_path(matches: &ArgMatches) -> String {
matches.get_one::<String>("ignore-path").map_or(".eslintignore".to_string(), ToOwned::to_owned)
}
fn get_ignore_pattern(matches: &ArgMatches) -> Vec<Pattern> {
let mut result = vec![];
let Some(ignore_pattern) = matches.get_many::<String>("ignore-pattern") else {return result};
for pattern in ignore_pattern {
if let Ok(pattern) = Pattern::new(pattern) {
result.push(pattern);
}
}
result
}

View file

@ -3,14 +3,14 @@ use std::path::Path;
use ignore::{DirEntry, WalkBuilder};
use oxc_ast::VALID_EXTENSIONS;
use crate::CliOptions;
use crate::LintOptions;
pub struct Walk {
inner: ignore::Walk,
}
impl Walk {
pub fn new<P: AsRef<Path>>(path: P, options: &CliOptions) -> Self {
pub fn new<P: AsRef<Path>>(path: P, options: &LintOptions) -> Self {
let mut inner = WalkBuilder::new(path);
if !options.no_ignore {
inner.add_custom_ignore_filename(&options.ignore_path);