mirror of
https://github.com/denoland/deno.git
synced 2025-08-04 19:08:15 +00:00
parent
48c6f71787
commit
84c793275b
17 changed files with 917 additions and 1244 deletions
|
@ -1,17 +1,19 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::args::BenchOptions;
|
||||
use crate::args::BenchFlags;
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::Flags;
|
||||
use crate::colors;
|
||||
use crate::display::write_json_to_stdout;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::factory::CliFactoryBuilder;
|
||||
use crate::graph_util::graph_valid_with_cli_options;
|
||||
use crate::graph_util::has_graph_root_local_dependent_changed;
|
||||
use crate::module_loader::ModuleLoadPreparer;
|
||||
use crate::ops;
|
||||
use crate::tools::test::format_test_error;
|
||||
use crate::tools::test::TestFilter;
|
||||
use crate::util::file_watcher;
|
||||
use crate::util::file_watcher::ResolutionResult;
|
||||
use crate::util::fs::collect_specifiers;
|
||||
use crate::util::path::is_supported_ext;
|
||||
use crate::version::get_user_agent;
|
||||
|
@ -22,7 +24,6 @@ use deno_core::error::AnyError;
|
|||
use deno_core::error::JsError;
|
||||
use deno_core::futures::future;
|
||||
use deno_core::futures::stream;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::futures::StreamExt;
|
||||
use deno_core::located_script_name;
|
||||
use deno_core::serde_v8;
|
||||
|
@ -40,7 +41,6 @@ use serde::Deserialize;
|
|||
use serde::Serialize;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::unbounded_channel;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
@ -630,9 +630,11 @@ fn is_supported_bench_path(path: &Path) -> bool {
|
|||
}
|
||||
|
||||
pub async fn run_benchmarks(
|
||||
cli_options: CliOptions,
|
||||
bench_options: BenchOptions,
|
||||
flags: Flags,
|
||||
bench_flags: BenchFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let cli_options = CliOptions::from_flags(flags)?;
|
||||
let bench_options = cli_options.resolve_bench_options(bench_flags)?;
|
||||
let factory = CliFactory::from_cli_options(Arc::new(cli_options));
|
||||
let cli_options = factory.cli_options();
|
||||
// Various bench files should not share the same permissions in terms of
|
||||
|
@ -679,169 +681,102 @@ pub async fn run_benchmarks(
|
|||
|
||||
// TODO(bartlomieju): heavy duplication of code with `cli/tools/test.rs`
|
||||
pub async fn run_benchmarks_with_watch(
|
||||
cli_options: CliOptions,
|
||||
bench_options: BenchOptions,
|
||||
flags: Flags,
|
||||
bench_flags: BenchFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let factory = CliFactory::from_cli_options(Arc::new(cli_options));
|
||||
let cli_options = factory.cli_options();
|
||||
let module_graph_builder = factory.module_graph_builder().await?;
|
||||
let file_watcher = factory.file_watcher()?;
|
||||
let module_load_preparer = factory.module_load_preparer().await?;
|
||||
// Various bench files should not share the same permissions in terms of
|
||||
// `PermissionsContainer` - otherwise granting/revoking permissions in one
|
||||
// file would have impact on other files, which is undesirable.
|
||||
let permissions =
|
||||
Permissions::from_options(&cli_options.permissions_options())?;
|
||||
let graph_kind = cli_options.type_check_mode().as_graph_kind();
|
||||
|
||||
let resolver = |changed: Option<Vec<PathBuf>>| {
|
||||
let paths_to_watch = bench_options.files.include.clone();
|
||||
let paths_to_watch_clone = paths_to_watch.clone();
|
||||
let files_changed = changed.is_some();
|
||||
let bench_options = &bench_options;
|
||||
let module_graph_builder = module_graph_builder.clone();
|
||||
let cli_options = cli_options.clone();
|
||||
|
||||
async move {
|
||||
let bench_modules =
|
||||
collect_specifiers(&bench_options.files, is_supported_bench_path)?;
|
||||
|
||||
let mut paths_to_watch = paths_to_watch_clone;
|
||||
let mut modules_to_reload = if files_changed {
|
||||
Vec::new()
|
||||
} else {
|
||||
bench_modules.clone()
|
||||
};
|
||||
let graph = module_graph_builder
|
||||
.create_graph(graph_kind, bench_modules.clone())
|
||||
.await?;
|
||||
graph_valid_with_cli_options(&graph, &bench_modules, &cli_options)?;
|
||||
|
||||
// TODO(@kitsonk) - This should be totally derivable from the graph.
|
||||
for specifier in bench_modules {
|
||||
fn get_dependencies<'a>(
|
||||
graph: &'a deno_graph::ModuleGraph,
|
||||
maybe_module: Option<&'a deno_graph::Module>,
|
||||
// This needs to be accessible to skip getting dependencies if they're already there,
|
||||
// otherwise this will cause a stack overflow with circular dependencies
|
||||
output: &mut HashSet<&'a ModuleSpecifier>,
|
||||
) {
|
||||
if let Some(module) = maybe_module.and_then(|m| m.esm()) {
|
||||
for dep in module.dependencies.values() {
|
||||
if let Some(specifier) = &dep.get_code() {
|
||||
if !output.contains(specifier) {
|
||||
output.insert(specifier);
|
||||
get_dependencies(graph, graph.get(specifier), output);
|
||||
}
|
||||
}
|
||||
if let Some(specifier) = &dep.get_type() {
|
||||
if !output.contains(specifier) {
|
||||
output.insert(specifier);
|
||||
get_dependencies(graph, graph.get(specifier), output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This bench module and all it's dependencies
|
||||
let mut modules = HashSet::new();
|
||||
modules.insert(&specifier);
|
||||
get_dependencies(&graph, graph.get(&specifier), &mut modules);
|
||||
|
||||
paths_to_watch.extend(
|
||||
modules
|
||||
.iter()
|
||||
.filter_map(|specifier| specifier.to_file_path().ok()),
|
||||
);
|
||||
|
||||
if let Some(changed) = &changed {
|
||||
for path in changed
|
||||
.iter()
|
||||
.filter_map(|path| ModuleSpecifier::from_file_path(path).ok())
|
||||
{
|
||||
if modules.contains(&path) {
|
||||
modules_to_reload.push(specifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((paths_to_watch, modules_to_reload))
|
||||
}
|
||||
.map(move |result| {
|
||||
if files_changed
|
||||
&& matches!(result, Ok((_, ref modules)) if modules.is_empty())
|
||||
{
|
||||
ResolutionResult::Ignore
|
||||
} else {
|
||||
match result {
|
||||
Ok((paths_to_watch, modules_to_reload)) => {
|
||||
ResolutionResult::Restart {
|
||||
paths_to_watch,
|
||||
result: Ok(modules_to_reload),
|
||||
}
|
||||
}
|
||||
Err(e) => ResolutionResult::Restart {
|
||||
paths_to_watch,
|
||||
result: Err(e),
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let create_cli_main_worker_factory =
|
||||
factory.create_cli_main_worker_factory_func().await?;
|
||||
let operation = |modules_to_reload: Vec<ModuleSpecifier>| {
|
||||
let permissions = &permissions;
|
||||
let bench_options = &bench_options;
|
||||
file_watcher.reset();
|
||||
let module_load_preparer = module_load_preparer.clone();
|
||||
let cli_options = cli_options.clone();
|
||||
let create_cli_main_worker_factory = create_cli_main_worker_factory.clone();
|
||||
|
||||
async move {
|
||||
let worker_factory = Arc::new(create_cli_main_worker_factory());
|
||||
let specifiers =
|
||||
collect_specifiers(&bench_options.files, is_supported_bench_path)?
|
||||
.into_iter()
|
||||
.filter(|specifier| modules_to_reload.contains(specifier))
|
||||
.collect::<Vec<ModuleSpecifier>>();
|
||||
|
||||
check_specifiers(&cli_options, &module_load_preparer, specifiers.clone())
|
||||
.await?;
|
||||
|
||||
if bench_options.no_run {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let log_level = cli_options.log_level();
|
||||
bench_specifiers(
|
||||
worker_factory,
|
||||
permissions,
|
||||
specifiers,
|
||||
BenchSpecifierOptions {
|
||||
filter: TestFilter::from_flag(&bench_options.filter),
|
||||
json: bench_options.json,
|
||||
log_level,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
let clear_screen = !cli_options.no_clear_screen();
|
||||
let clear_screen = !flags.no_clear_screen;
|
||||
file_watcher::watch_func(
|
||||
resolver,
|
||||
operation,
|
||||
flags,
|
||||
file_watcher::PrintConfig {
|
||||
job_name: "Bench".to_string(),
|
||||
clear_screen,
|
||||
},
|
||||
move |flags, sender, changed_paths| {
|
||||
let bench_flags = bench_flags.clone();
|
||||
Ok(async move {
|
||||
let factory = CliFactoryBuilder::new()
|
||||
.with_watcher(sender.clone())
|
||||
.build_from_flags(flags)
|
||||
.await?;
|
||||
let cli_options = factory.cli_options();
|
||||
let bench_options = cli_options.resolve_bench_options(bench_flags)?;
|
||||
|
||||
if let Some(watch_paths) = cli_options.watch_paths() {
|
||||
let _ = sender.send(watch_paths);
|
||||
}
|
||||
let _ = sender.send(bench_options.files.include.clone());
|
||||
|
||||
let graph_kind = cli_options.type_check_mode().as_graph_kind();
|
||||
let module_graph_builder = factory.module_graph_builder().await?;
|
||||
let module_load_preparer = factory.module_load_preparer().await?;
|
||||
|
||||
let bench_modules =
|
||||
collect_specifiers(&bench_options.files, is_supported_bench_path)?;
|
||||
|
||||
// Various bench files should not share the same permissions in terms of
|
||||
// `PermissionsContainer` - otherwise granting/revoking permissions in one
|
||||
// file would have impact on other files, which is undesirable.
|
||||
let permissions =
|
||||
Permissions::from_options(&cli_options.permissions_options())?;
|
||||
|
||||
let graph = module_graph_builder
|
||||
.create_graph(graph_kind, bench_modules.clone())
|
||||
.await?;
|
||||
graph_valid_with_cli_options(&graph, &bench_modules, cli_options)?;
|
||||
|
||||
let bench_modules_to_reload = if let Some(changed_paths) = changed_paths
|
||||
{
|
||||
let changed_specifiers = changed_paths
|
||||
.into_iter()
|
||||
.filter_map(|p| ModuleSpecifier::from_file_path(p).ok())
|
||||
.collect::<HashSet<_>>();
|
||||
let mut result = Vec::new();
|
||||
for bench_module_specifier in bench_modules {
|
||||
if has_graph_root_local_dependent_changed(
|
||||
&graph,
|
||||
&bench_module_specifier,
|
||||
&changed_specifiers,
|
||||
) {
|
||||
result.push(bench_module_specifier.clone());
|
||||
}
|
||||
}
|
||||
result
|
||||
} else {
|
||||
bench_modules.clone()
|
||||
};
|
||||
|
||||
let worker_factory =
|
||||
Arc::new(factory.create_cli_main_worker_factory().await?);
|
||||
|
||||
let specifiers =
|
||||
collect_specifiers(&bench_options.files, is_supported_bench_path)?
|
||||
.into_iter()
|
||||
.filter(|specifier| bench_modules_to_reload.contains(specifier))
|
||||
.collect::<Vec<ModuleSpecifier>>();
|
||||
|
||||
check_specifiers(cli_options, module_load_preparer, specifiers.clone())
|
||||
.await?;
|
||||
|
||||
if bench_options.no_run {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let log_level = cli_options.log_level();
|
||||
bench_specifiers(
|
||||
worker_factory,
|
||||
&permissions,
|
||||
specifiers,
|
||||
BenchSpecifierOptions {
|
||||
filter: TestFilter::from_flag(&bench_options.filter),
|
||||
json: bench_options.json,
|
||||
log_level,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_graph::Module;
|
||||
use deno_runtime::colors;
|
||||
|
||||
|
@ -13,17 +11,15 @@ use crate::args::CliOptions;
|
|||
use crate::args::Flags;
|
||||
use crate::args::TsConfigType;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::factory::CliFactoryBuilder;
|
||||
use crate::graph_util::error_for_any_npm_specifier;
|
||||
use crate::util;
|
||||
use crate::util::display;
|
||||
use crate::util::file_watcher::ResolutionResult;
|
||||
|
||||
pub async fn bundle(
|
||||
flags: Flags,
|
||||
bundle_flags: BundleFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let cli_options = Arc::new(CliOptions::from_flags(flags)?);
|
||||
|
||||
log::info!(
|
||||
"{} \"deno bundle\" is deprecated and will be removed in the future.",
|
||||
colors::yellow("Warning"),
|
||||
|
@ -32,122 +28,115 @@ pub async fn bundle(
|
|||
"Use alternative bundlers like \"deno_emit\", \"esbuild\" or \"rollup\" instead."
|
||||
);
|
||||
|
||||
let module_specifier = cli_options.resolve_main_module()?;
|
||||
|
||||
let resolver = |_| {
|
||||
let cli_options = cli_options.clone();
|
||||
let module_specifier = &module_specifier;
|
||||
async move {
|
||||
log::debug!(">>>>> bundle START");
|
||||
let factory = CliFactory::from_cli_options(cli_options);
|
||||
let module_graph_builder = factory.module_graph_builder().await?;
|
||||
let cli_options = factory.cli_options();
|
||||
|
||||
let graph = module_graph_builder
|
||||
.create_graph_and_maybe_check(vec![module_specifier.clone()])
|
||||
.await?;
|
||||
|
||||
let mut paths_to_watch: Vec<PathBuf> = graph
|
||||
.specifiers()
|
||||
.filter_map(|(_, r)| {
|
||||
r.ok().and_then(|module| match module {
|
||||
Module::Esm(m) => m.specifier.to_file_path().ok(),
|
||||
Module::Json(m) => m.specifier.to_file_path().ok(),
|
||||
// nothing to watch
|
||||
Module::Node(_) | Module::Npm(_) | Module::External(_) => None,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let Ok(Some(import_map_path)) = cli_options
|
||||
.resolve_import_map_specifier()
|
||||
.map(|ms| ms.and_then(|ref s| s.to_file_path().ok()))
|
||||
{
|
||||
paths_to_watch.push(import_map_path);
|
||||
}
|
||||
|
||||
Ok((paths_to_watch, graph, cli_options.clone()))
|
||||
}
|
||||
.map(move |result| match result {
|
||||
Ok((paths_to_watch, graph, ps)) => ResolutionResult::Restart {
|
||||
paths_to_watch,
|
||||
result: Ok((ps, graph)),
|
||||
},
|
||||
Err(e) => ResolutionResult::Restart {
|
||||
paths_to_watch: vec![module_specifier.to_file_path().unwrap()],
|
||||
result: Err(e),
|
||||
},
|
||||
})
|
||||
};
|
||||
|
||||
let operation =
|
||||
|(cli_options, graph): (Arc<CliOptions>, Arc<deno_graph::ModuleGraph>)| {
|
||||
let out_file = &bundle_flags.out_file;
|
||||
async move {
|
||||
// at the moment, we don't support npm specifiers in deno bundle, so show an error
|
||||
error_for_any_npm_specifier(&graph)?;
|
||||
|
||||
let bundle_output = bundle_module_graph(graph.as_ref(), &cli_options)?;
|
||||
log::debug!(">>>>> bundle END");
|
||||
|
||||
if let Some(out_file) = out_file {
|
||||
let output_bytes = bundle_output.code.as_bytes();
|
||||
let output_len = output_bytes.len();
|
||||
util::fs::write_file(out_file, output_bytes, 0o644)?;
|
||||
log::info!(
|
||||
"{} {:?} ({})",
|
||||
colors::green("Emit"),
|
||||
out_file,
|
||||
colors::gray(display::human_size(output_len as f64))
|
||||
);
|
||||
if let Some(bundle_map) = bundle_output.maybe_map {
|
||||
let map_bytes = bundle_map.as_bytes();
|
||||
let map_len = map_bytes.len();
|
||||
let ext = if let Some(curr_ext) = out_file.extension() {
|
||||
format!("{}.map", curr_ext.to_string_lossy())
|
||||
} else {
|
||||
"map".to_string()
|
||||
};
|
||||
let map_out_file = out_file.with_extension(ext);
|
||||
util::fs::write_file(&map_out_file, map_bytes, 0o644)?;
|
||||
log::info!(
|
||||
"{} {:?} ({})",
|
||||
colors::green("Emit"),
|
||||
map_out_file,
|
||||
colors::gray(display::human_size(map_len as f64))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
println!("{}", bundle_output.code);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
if cli_options.watch_paths().is_some() {
|
||||
if flags.watch.is_some() {
|
||||
let clear_screen = !flags.no_clear_screen;
|
||||
util::file_watcher::watch_func(
|
||||
resolver,
|
||||
operation,
|
||||
flags,
|
||||
util::file_watcher::PrintConfig {
|
||||
job_name: "Bundle".to_string(),
|
||||
clear_screen: !cli_options.no_clear_screen(),
|
||||
clear_screen,
|
||||
},
|
||||
move |flags, sender, _changed_paths| {
|
||||
let bundle_flags = bundle_flags.clone();
|
||||
Ok(async move {
|
||||
let factory = CliFactoryBuilder::new()
|
||||
.with_watcher(sender.clone())
|
||||
.build_from_flags(flags)
|
||||
.await?;
|
||||
let cli_options = factory.cli_options();
|
||||
|
||||
if let Some(watch_paths) = cli_options.watch_paths() {
|
||||
let _ = sender.send(watch_paths);
|
||||
}
|
||||
|
||||
bundle_action(factory, &bundle_flags).await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
let module_graph =
|
||||
if let ResolutionResult::Restart { result, .. } = resolver(None).await {
|
||||
result?
|
||||
} else {
|
||||
unreachable!();
|
||||
};
|
||||
operation(module_graph).await?;
|
||||
let factory = CliFactory::from_flags(flags).await?;
|
||||
bundle_action(factory, &bundle_flags).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn bundle_action(
|
||||
factory: CliFactory,
|
||||
bundle_flags: &BundleFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let cli_options = factory.cli_options();
|
||||
let module_specifier = cli_options.resolve_main_module()?;
|
||||
log::debug!(">>>>> bundle START");
|
||||
let module_graph_builder = factory.module_graph_builder().await?;
|
||||
let cli_options = factory.cli_options();
|
||||
|
||||
let graph = module_graph_builder
|
||||
.create_graph_and_maybe_check(vec![module_specifier.clone()])
|
||||
.await?;
|
||||
|
||||
let mut paths_to_watch: Vec<PathBuf> = graph
|
||||
.specifiers()
|
||||
.filter_map(|(_, r)| {
|
||||
r.ok().and_then(|module| match module {
|
||||
Module::Esm(m) => m.specifier.to_file_path().ok(),
|
||||
Module::Json(m) => m.specifier.to_file_path().ok(),
|
||||
// nothing to watch
|
||||
Module::Node(_) | Module::Npm(_) | Module::External(_) => None,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let Ok(Some(import_map_path)) = cli_options
|
||||
.resolve_import_map_specifier()
|
||||
.map(|ms| ms.and_then(|ref s| s.to_file_path().ok()))
|
||||
{
|
||||
paths_to_watch.push(import_map_path);
|
||||
}
|
||||
|
||||
// at the moment, we don't support npm specifiers in deno bundle, so show an error
|
||||
error_for_any_npm_specifier(&graph)?;
|
||||
|
||||
let bundle_output = bundle_module_graph(graph.as_ref(), cli_options)?;
|
||||
log::debug!(">>>>> bundle END");
|
||||
let out_file = &bundle_flags.out_file;
|
||||
|
||||
if let Some(out_file) = out_file {
|
||||
let output_bytes = bundle_output.code.as_bytes();
|
||||
let output_len = output_bytes.len();
|
||||
util::fs::write_file(out_file, output_bytes, 0o644)?;
|
||||
log::info!(
|
||||
"{} {:?} ({})",
|
||||
colors::green("Emit"),
|
||||
out_file,
|
||||
colors::gray(display::human_size(output_len as f64))
|
||||
);
|
||||
if let Some(bundle_map) = bundle_output.maybe_map {
|
||||
let map_bytes = bundle_map.as_bytes();
|
||||
let map_len = map_bytes.len();
|
||||
let ext = if let Some(curr_ext) = out_file.extension() {
|
||||
format!("{}.map", curr_ext.to_string_lossy())
|
||||
} else {
|
||||
"map".to_string()
|
||||
};
|
||||
let map_out_file = out_file.with_extension(ext);
|
||||
util::fs::write_file(&map_out_file, map_bytes, 0o644)?;
|
||||
log::info!(
|
||||
"{} {:?} ({})",
|
||||
colors::green("Emit"),
|
||||
map_out_file,
|
||||
colors::gray(display::human_size(map_len as f64))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
println!("{}", bundle_output.code);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bundle_module_graph(
|
||||
graph: &deno_graph::ModuleGraph,
|
||||
cli_options: &CliOptions,
|
||||
|
|
147
cli/tools/fmt.rs
147
cli/tools/fmt.rs
|
@ -9,6 +9,8 @@
|
|||
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::FilesConfig;
|
||||
use crate::args::Flags;
|
||||
use crate::args::FmtFlags;
|
||||
use crate::args::FmtOptions;
|
||||
use crate::args::FmtOptionsConfig;
|
||||
use crate::args::ProseWrap;
|
||||
|
@ -16,7 +18,6 @@ use crate::colors;
|
|||
use crate::factory::CliFactory;
|
||||
use crate::util::diff::diff;
|
||||
use crate::util::file_watcher;
|
||||
use crate::util::file_watcher::ResolutionResult;
|
||||
use crate::util::fs::FileCollector;
|
||||
use crate::util::path::get_extension;
|
||||
use crate::util::text_encoding;
|
||||
|
@ -46,11 +47,10 @@ use std::sync::Arc;
|
|||
use crate::cache::IncrementalCache;
|
||||
|
||||
/// Format JavaScript/TypeScript files.
|
||||
pub async fn format(
|
||||
cli_options: CliOptions,
|
||||
fmt_options: FmtOptions,
|
||||
) -> Result<(), AnyError> {
|
||||
if fmt_options.is_stdin {
|
||||
pub async fn format(flags: Flags, fmt_flags: FmtFlags) -> Result<(), AnyError> {
|
||||
if fmt_flags.is_stdin() {
|
||||
let cli_options = CliOptions::from_flags(flags)?;
|
||||
let fmt_options = cli_options.resolve_fmt_options(fmt_flags)?;
|
||||
return format_stdin(
|
||||
fmt_options,
|
||||
cli_options
|
||||
|
@ -61,90 +61,93 @@ pub async fn format(
|
|||
);
|
||||
}
|
||||
|
||||
let files = fmt_options.files;
|
||||
let check = fmt_options.check;
|
||||
let fmt_config_options = fmt_options.options;
|
||||
|
||||
let resolver = |changed: Option<Vec<PathBuf>>| {
|
||||
let files_changed = changed.is_some();
|
||||
|
||||
let result = collect_fmt_files(&files).map(|files| {
|
||||
let refmt_files = if let Some(paths) = changed {
|
||||
if check {
|
||||
files
|
||||
.iter()
|
||||
.any(|path| paths.contains(path))
|
||||
.then_some(files)
|
||||
.unwrap_or_else(|| [].to_vec())
|
||||
} else {
|
||||
files
|
||||
.into_iter()
|
||||
.filter(|path| paths.contains(path))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
} else {
|
||||
files
|
||||
};
|
||||
(refmt_files, fmt_config_options.clone())
|
||||
});
|
||||
|
||||
let paths_to_watch = files.include.clone();
|
||||
async move {
|
||||
if files_changed
|
||||
&& matches!(result, Ok((ref files, _)) if files.is_empty())
|
||||
{
|
||||
ResolutionResult::Ignore
|
||||
} else {
|
||||
ResolutionResult::Restart {
|
||||
paths_to_watch,
|
||||
result,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let factory = CliFactory::from_cli_options(Arc::new(cli_options));
|
||||
let cli_options = factory.cli_options();
|
||||
let caches = factory.caches()?;
|
||||
let operation = |(paths, fmt_options): (Vec<PathBuf>, FmtOptionsConfig)| async {
|
||||
let incremental_cache = Arc::new(IncrementalCache::new(
|
||||
caches.fmt_incremental_cache_db(),
|
||||
&fmt_options,
|
||||
&paths,
|
||||
));
|
||||
if check {
|
||||
check_source_files(paths, fmt_options, incremental_cache.clone()).await?;
|
||||
} else {
|
||||
format_source_files(paths, fmt_options, incremental_cache.clone())
|
||||
.await?;
|
||||
}
|
||||
incremental_cache.wait_completion().await;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
if cli_options.watch_paths().is_some() {
|
||||
if flags.watch.is_some() {
|
||||
let clear_screen = !flags.no_clear_screen;
|
||||
file_watcher::watch_func(
|
||||
resolver,
|
||||
operation,
|
||||
flags,
|
||||
file_watcher::PrintConfig {
|
||||
job_name: "Fmt".to_string(),
|
||||
clear_screen: !cli_options.no_clear_screen(),
|
||||
clear_screen,
|
||||
},
|
||||
move |flags, sender, changed_paths| {
|
||||
let fmt_flags = fmt_flags.clone();
|
||||
Ok(async move {
|
||||
let factory = CliFactory::from_flags(flags).await?;
|
||||
let cli_options = factory.cli_options();
|
||||
let fmt_options = cli_options.resolve_fmt_options(fmt_flags)?;
|
||||
let files =
|
||||
collect_fmt_files(&fmt_options.files).and_then(|files| {
|
||||
if files.is_empty() {
|
||||
Err(generic_error("No target files found."))
|
||||
} else {
|
||||
Ok(files)
|
||||
}
|
||||
})?;
|
||||
_ = sender.send(files.clone());
|
||||
let refmt_files = if let Some(paths) = changed_paths {
|
||||
if fmt_options.check {
|
||||
// check all files on any changed (https://github.com/denoland/deno/issues/12446)
|
||||
files
|
||||
.iter()
|
||||
.any(|path| paths.contains(path))
|
||||
.then_some(files)
|
||||
.unwrap_or_else(|| [].to_vec())
|
||||
} else {
|
||||
files
|
||||
.into_iter()
|
||||
.filter(|path| paths.contains(path))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
} else {
|
||||
files
|
||||
};
|
||||
format_files(factory, fmt_options, refmt_files).await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
let files = collect_fmt_files(&files).and_then(|files| {
|
||||
let factory = CliFactory::from_flags(flags).await?;
|
||||
let cli_options = factory.cli_options();
|
||||
let fmt_options = cli_options.resolve_fmt_options(fmt_flags)?;
|
||||
let files = collect_fmt_files(&fmt_options.files).and_then(|files| {
|
||||
if files.is_empty() {
|
||||
Err(generic_error("No target files found."))
|
||||
} else {
|
||||
Ok(files)
|
||||
}
|
||||
})?;
|
||||
operation((files, fmt_config_options)).await?;
|
||||
format_files(factory, fmt_options, files).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn format_files(
|
||||
factory: CliFactory,
|
||||
fmt_options: FmtOptions,
|
||||
paths: Vec<PathBuf>,
|
||||
) -> Result<(), AnyError> {
|
||||
let caches = factory.caches()?;
|
||||
let check = fmt_options.check;
|
||||
let incremental_cache = Arc::new(IncrementalCache::new(
|
||||
caches.fmt_incremental_cache_db(),
|
||||
&fmt_options.options,
|
||||
&paths,
|
||||
));
|
||||
if check {
|
||||
check_source_files(paths, fmt_options.options, incremental_cache.clone())
|
||||
.await?;
|
||||
} else {
|
||||
format_source_files(paths, fmt_options.options, incremental_cache.clone())
|
||||
.await?;
|
||||
}
|
||||
incremental_cache.wait_completion().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collect_fmt_files(files: &FilesConfig) -> Result<Vec<PathBuf>, AnyError> {
|
||||
FileCollector::new(is_supported_ext_fmt)
|
||||
.ignore_git_folder()
|
||||
|
|
|
@ -2,12 +2,9 @@
|
|||
|
||||
//! This module provides file linting utilities using
|
||||
//! [`deno_lint`](https://github.com/denoland/deno_lint).
|
||||
//!
|
||||
//! At the moment it is only consumed using CLI but in
|
||||
//! the future it can be easily extended to provide
|
||||
//! the same functions as ops available in JS runtime.
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::FilesConfig;
|
||||
use crate::args::Flags;
|
||||
use crate::args::LintFlags;
|
||||
use crate::args::LintOptions;
|
||||
use crate::args::LintReporterKind;
|
||||
use crate::args::LintRulesConfig;
|
||||
|
@ -15,9 +12,9 @@ use crate::colors;
|
|||
use crate::factory::CliFactory;
|
||||
use crate::tools::fmt::run_parallelized;
|
||||
use crate::util::file_watcher;
|
||||
use crate::util::file_watcher::ResolutionResult;
|
||||
use crate::util::fs::FileCollector;
|
||||
use crate::util::path::is_supported_ext;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::error::generic_error;
|
||||
|
@ -38,8 +35,6 @@ use std::io::stdin;
|
|||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
|
@ -55,133 +50,70 @@ fn create_reporter(kind: LintReporterKind) -> Box<dyn LintReporter + Send> {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn lint(
|
||||
cli_options: CliOptions,
|
||||
lint_options: LintOptions,
|
||||
) -> Result<(), AnyError> {
|
||||
// Try to get lint rules. If none were set use recommended rules.
|
||||
let lint_rules = get_configured_rules(lint_options.rules);
|
||||
|
||||
if lint_rules.is_empty() {
|
||||
bail!("No rules have been configured")
|
||||
}
|
||||
|
||||
let files = lint_options.files;
|
||||
let reporter_kind = lint_options.reporter_kind;
|
||||
|
||||
let resolver = |changed: Option<Vec<PathBuf>>| {
|
||||
let files_changed = changed.is_some();
|
||||
let result = collect_lint_files(&files).map(|files| {
|
||||
if let Some(paths) = changed {
|
||||
files
|
||||
.iter()
|
||||
.any(|path| paths.contains(path))
|
||||
.then_some(files)
|
||||
.unwrap_or_else(|| [].to_vec())
|
||||
} else {
|
||||
files
|
||||
}
|
||||
});
|
||||
|
||||
let paths_to_watch = files.include.clone();
|
||||
|
||||
async move {
|
||||
if files_changed && matches!(result, Ok(ref files) if files.is_empty()) {
|
||||
ResolutionResult::Ignore
|
||||
} else {
|
||||
ResolutionResult::Restart {
|
||||
paths_to_watch,
|
||||
result,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let has_error = Arc::new(AtomicBool::new(false));
|
||||
let factory = CliFactory::from_cli_options(Arc::new(cli_options));
|
||||
let cli_options = factory.cli_options();
|
||||
let caches = factory.caches()?;
|
||||
let operation = |paths: Vec<PathBuf>| async {
|
||||
let incremental_cache = Arc::new(IncrementalCache::new(
|
||||
caches.lint_incremental_cache_db(),
|
||||
// use a hash of the rule names in order to bust the cache
|
||||
&{
|
||||
// ensure this is stable by sorting it
|
||||
let mut names = lint_rules.iter().map(|r| r.code()).collect::<Vec<_>>();
|
||||
names.sort_unstable();
|
||||
names
|
||||
},
|
||||
&paths,
|
||||
));
|
||||
let target_files_len = paths.len();
|
||||
let reporter_lock =
|
||||
Arc::new(Mutex::new(create_reporter(reporter_kind.clone())));
|
||||
|
||||
run_parallelized(paths, {
|
||||
let has_error = has_error.clone();
|
||||
let lint_rules = lint_rules.clone();
|
||||
let reporter_lock = reporter_lock.clone();
|
||||
let incremental_cache = incremental_cache.clone();
|
||||
move |file_path| {
|
||||
let file_text = fs::read_to_string(&file_path)?;
|
||||
|
||||
// don't bother rechecking this file if it didn't have any diagnostics before
|
||||
if incremental_cache.is_file_same(&file_path, &file_text) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let r = lint_file(&file_path, file_text, lint_rules);
|
||||
if let Ok((file_diagnostics, file_text)) = &r {
|
||||
if file_diagnostics.is_empty() {
|
||||
// update the incremental cache if there were no diagnostics
|
||||
incremental_cache.update_file(&file_path, file_text)
|
||||
}
|
||||
}
|
||||
|
||||
handle_lint_result(
|
||||
&file_path.to_string_lossy(),
|
||||
r,
|
||||
reporter_lock.clone(),
|
||||
has_error,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
incremental_cache.wait_completion().await;
|
||||
reporter_lock.lock().unwrap().close(target_files_len);
|
||||
|
||||
Ok(())
|
||||
};
|
||||
if cli_options.watch_paths().is_some() {
|
||||
if lint_options.is_stdin {
|
||||
pub async fn lint(flags: Flags, lint_flags: LintFlags) -> Result<(), AnyError> {
|
||||
if flags.watch.is_some() {
|
||||
if lint_flags.is_stdin() {
|
||||
return Err(generic_error(
|
||||
"Lint watch on standard input is not supported.",
|
||||
));
|
||||
}
|
||||
let clear_screen = !flags.no_clear_screen;
|
||||
file_watcher::watch_func(
|
||||
resolver,
|
||||
operation,
|
||||
flags,
|
||||
file_watcher::PrintConfig {
|
||||
job_name: "Lint".to_string(),
|
||||
clear_screen: !cli_options.no_clear_screen(),
|
||||
clear_screen,
|
||||
},
|
||||
move |flags, sender, changed_paths| {
|
||||
let lint_flags = lint_flags.clone();
|
||||
Ok(async move {
|
||||
let factory = CliFactory::from_flags(flags).await?;
|
||||
let cli_options = factory.cli_options();
|
||||
let lint_options = cli_options.resolve_lint_options(lint_flags)?;
|
||||
let files =
|
||||
collect_lint_files(&lint_options.files).and_then(|files| {
|
||||
if files.is_empty() {
|
||||
Err(generic_error("No target files found."))
|
||||
} else {
|
||||
Ok(files)
|
||||
}
|
||||
})?;
|
||||
_ = sender.send(files.clone());
|
||||
|
||||
let lint_paths = if let Some(paths) = changed_paths {
|
||||
// lint all files on any changed (https://github.com/denoland/deno/issues/12446)
|
||||
files
|
||||
.iter()
|
||||
.any(|path| paths.contains(path))
|
||||
.then_some(files)
|
||||
.unwrap_or_else(|| [].to_vec())
|
||||
} else {
|
||||
files
|
||||
};
|
||||
|
||||
lint_files(factory, lint_options, lint_paths).await?;
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
if lint_options.is_stdin {
|
||||
let factory = CliFactory::from_flags(flags).await?;
|
||||
let cli_options = factory.cli_options();
|
||||
let is_stdin = lint_flags.is_stdin();
|
||||
let lint_options = cli_options.resolve_lint_options(lint_flags)?;
|
||||
let files = &lint_options.files;
|
||||
let success = if is_stdin {
|
||||
let reporter_kind = lint_options.reporter_kind;
|
||||
let reporter_lock = Arc::new(Mutex::new(create_reporter(reporter_kind)));
|
||||
let lint_rules = get_config_rules_err_empty(lint_options.rules)?;
|
||||
let r = lint_stdin(lint_rules);
|
||||
handle_lint_result(
|
||||
STDIN_FILE_NAME,
|
||||
r,
|
||||
reporter_lock.clone(),
|
||||
has_error.clone(),
|
||||
);
|
||||
let success =
|
||||
handle_lint_result(STDIN_FILE_NAME, r, reporter_lock.clone());
|
||||
reporter_lock.lock().unwrap().close(1);
|
||||
success
|
||||
} else {
|
||||
let target_files = collect_lint_files(&files).and_then(|files| {
|
||||
let target_files = collect_lint_files(files).and_then(|files| {
|
||||
if files.is_empty() {
|
||||
Err(generic_error("No target files found."))
|
||||
} else {
|
||||
|
@ -189,10 +121,9 @@ pub async fn lint(
|
|||
}
|
||||
})?;
|
||||
debug!("Found {} files", target_files.len());
|
||||
operation(target_files).await?;
|
||||
lint_files(factory, lint_options, target_files).await?
|
||||
};
|
||||
let has_error = has_error.load(Ordering::Relaxed);
|
||||
if has_error {
|
||||
if !success {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -200,6 +131,70 @@ pub async fn lint(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn lint_files(
|
||||
factory: CliFactory,
|
||||
lint_options: LintOptions,
|
||||
paths: Vec<PathBuf>,
|
||||
) -> Result<bool, AnyError> {
|
||||
let caches = factory.caches()?;
|
||||
let lint_rules = get_config_rules_err_empty(lint_options.rules)?;
|
||||
let incremental_cache = Arc::new(IncrementalCache::new(
|
||||
caches.lint_incremental_cache_db(),
|
||||
// use a hash of the rule names in order to bust the cache
|
||||
&{
|
||||
// ensure this is stable by sorting it
|
||||
let mut names = lint_rules.iter().map(|r| r.code()).collect::<Vec<_>>();
|
||||
names.sort_unstable();
|
||||
names
|
||||
},
|
||||
&paths,
|
||||
));
|
||||
let target_files_len = paths.len();
|
||||
let reporter_kind = lint_options.reporter_kind;
|
||||
let reporter_lock =
|
||||
Arc::new(Mutex::new(create_reporter(reporter_kind.clone())));
|
||||
let has_error = Arc::new(AtomicFlag::default());
|
||||
|
||||
run_parallelized(paths, {
|
||||
let has_error = has_error.clone();
|
||||
let lint_rules = lint_rules.clone();
|
||||
let reporter_lock = reporter_lock.clone();
|
||||
let incremental_cache = incremental_cache.clone();
|
||||
move |file_path| {
|
||||
let file_text = fs::read_to_string(&file_path)?;
|
||||
|
||||
// don't bother rechecking this file if it didn't have any diagnostics before
|
||||
if incremental_cache.is_file_same(&file_path, &file_text) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let r = lint_file(&file_path, file_text, lint_rules);
|
||||
if let Ok((file_diagnostics, file_text)) = &r {
|
||||
if file_diagnostics.is_empty() {
|
||||
// update the incremental cache if there were no diagnostics
|
||||
incremental_cache.update_file(&file_path, file_text)
|
||||
}
|
||||
}
|
||||
|
||||
let success = handle_lint_result(
|
||||
&file_path.to_string_lossy(),
|
||||
r,
|
||||
reporter_lock.clone(),
|
||||
);
|
||||
if !success {
|
||||
has_error.raise();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
incremental_cache.wait_completion().await;
|
||||
reporter_lock.lock().unwrap().close(target_files_len);
|
||||
|
||||
Ok(!has_error.is_raised())
|
||||
}
|
||||
|
||||
fn collect_lint_files(files: &FilesConfig) -> Result<Vec<PathBuf>, AnyError> {
|
||||
FileCollector::new(is_supported_ext)
|
||||
.ignore_git_folder()
|
||||
|
@ -286,21 +281,20 @@ fn handle_lint_result(
|
|||
file_path: &str,
|
||||
result: Result<(Vec<LintDiagnostic>, String), AnyError>,
|
||||
reporter_lock: Arc<Mutex<Box<dyn LintReporter + Send>>>,
|
||||
has_error: Arc<AtomicBool>,
|
||||
) {
|
||||
) -> bool {
|
||||
let mut reporter = reporter_lock.lock().unwrap();
|
||||
|
||||
match result {
|
||||
Ok((mut file_diagnostics, source)) => {
|
||||
sort_diagnostics(&mut file_diagnostics);
|
||||
for d in file_diagnostics.iter() {
|
||||
has_error.store(true, Ordering::Relaxed);
|
||||
reporter.visit_diagnostic(d, source.split('\n').collect());
|
||||
}
|
||||
file_diagnostics.is_empty()
|
||||
}
|
||||
Err(err) => {
|
||||
has_error.store(true, Ordering::Relaxed);
|
||||
reporter.visit_error(file_path, &err);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -534,6 +528,16 @@ fn sort_diagnostics(diagnostics: &mut [LintDiagnostic]) {
|
|||
});
|
||||
}
|
||||
|
||||
fn get_config_rules_err_empty(
|
||||
rules: LintRulesConfig,
|
||||
) -> Result<Vec<&'static dyn LintRule>, AnyError> {
|
||||
let lint_rules = get_configured_rules(rules);
|
||||
if lint_rules.is_empty() {
|
||||
bail!("No rules have been configured")
|
||||
}
|
||||
Ok(lint_rules)
|
||||
}
|
||||
|
||||
pub fn get_configured_rules(
|
||||
rules: LintRulesConfig,
|
||||
) -> Vec<&'static dyn LintRule> {
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
use std::io::Read;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_runtime::permissions::Permissions;
|
||||
use deno_runtime::permissions::PermissionsContainer;
|
||||
|
@ -98,45 +97,42 @@ pub async fn run_from_stdin(flags: Flags) -> Result<i32, AnyError> {
|
|||
// TODO(bartlomieju): this function is not handling `exit_code` set by the runtime
|
||||
// code properly.
|
||||
async fn run_with_watch(flags: Flags) -> Result<i32, AnyError> {
|
||||
let (sender, receiver) = tokio::sync::mpsc::unbounded_channel();
|
||||
let factory = CliFactoryBuilder::new()
|
||||
.with_watcher(sender.clone())
|
||||
.build_from_flags(flags)
|
||||
.await?;
|
||||
let file_watcher = factory.file_watcher()?;
|
||||
let cli_options = factory.cli_options();
|
||||
let clear_screen = !cli_options.no_clear_screen();
|
||||
let main_module = cli_options.resolve_main_module()?;
|
||||
let clear_screen = !flags.no_clear_screen;
|
||||
|
||||
maybe_npm_install(&factory).await?;
|
||||
|
||||
let create_cli_main_worker_factory =
|
||||
factory.create_cli_main_worker_factory_func().await?;
|
||||
let operation = |main_module: ModuleSpecifier| {
|
||||
file_watcher.reset();
|
||||
let permissions = PermissionsContainer::new(Permissions::from_options(
|
||||
&cli_options.permissions_options(),
|
||||
)?);
|
||||
let create_cli_main_worker_factory = create_cli_main_worker_factory.clone();
|
||||
|
||||
Ok(async move {
|
||||
let worker = create_cli_main_worker_factory()
|
||||
.create_main_worker(main_module, permissions)
|
||||
.await?;
|
||||
worker.run_for_watcher().await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
};
|
||||
|
||||
util::file_watcher::watch_func2(
|
||||
receiver,
|
||||
operation,
|
||||
main_module,
|
||||
util::file_watcher::watch_func(
|
||||
flags,
|
||||
util::file_watcher::PrintConfig {
|
||||
job_name: "Process".to_string(),
|
||||
clear_screen,
|
||||
},
|
||||
move |flags, sender, _changed_paths| {
|
||||
Ok(async move {
|
||||
let factory = CliFactoryBuilder::new()
|
||||
.with_watcher(sender.clone())
|
||||
.build_from_flags(flags)
|
||||
.await?;
|
||||
let cli_options = factory.cli_options();
|
||||
let main_module = cli_options.resolve_main_module()?;
|
||||
|
||||
maybe_npm_install(&factory).await?;
|
||||
|
||||
if let Some(watch_paths) = cli_options.watch_paths() {
|
||||
let _ = sender.send(watch_paths);
|
||||
}
|
||||
|
||||
let permissions = PermissionsContainer::new(Permissions::from_options(
|
||||
&cli_options.permissions_options(),
|
||||
)?);
|
||||
let worker = factory
|
||||
.create_cli_main_worker_factory()
|
||||
.await?
|
||||
.create_main_worker(main_module, permissions)
|
||||
.await?;
|
||||
worker.run_for_watcher().await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -2,18 +2,20 @@
|
|||
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::FilesConfig;
|
||||
use crate::args::TestOptions;
|
||||
use crate::args::Flags;
|
||||
use crate::args::TestFlags;
|
||||
use crate::colors;
|
||||
use crate::display;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::factory::CliFactoryBuilder;
|
||||
use crate::file_fetcher::File;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::graph_util::graph_valid_with_cli_options;
|
||||
use crate::graph_util::has_graph_root_local_dependent_changed;
|
||||
use crate::module_loader::ModuleLoadPreparer;
|
||||
use crate::ops;
|
||||
use crate::util::checksum;
|
||||
use crate::util::file_watcher;
|
||||
use crate::util::file_watcher::ResolutionResult;
|
||||
use crate::util::fs::collect_specifiers;
|
||||
use crate::util::path::get_extension;
|
||||
use crate::util::path::is_supported_ext;
|
||||
|
@ -62,7 +64,6 @@ use std::io::Read;
|
|||
use std::io::Write;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
@ -1641,11 +1642,12 @@ async fn fetch_specifiers_with_test_mode(
|
|||
}
|
||||
|
||||
pub async fn run_tests(
|
||||
cli_options: CliOptions,
|
||||
test_options: TestOptions,
|
||||
flags: Flags,
|
||||
test_flags: TestFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let factory = CliFactory::from_cli_options(Arc::new(cli_options));
|
||||
let factory = CliFactory::from_flags(flags).await?;
|
||||
let cli_options = factory.cli_options();
|
||||
let test_options = cli_options.resolve_test_options(test_flags)?;
|
||||
let file_fetcher = factory.file_fetcher()?;
|
||||
let module_load_preparer = factory.module_load_preparer().await?;
|
||||
// Various test files should not share the same permissions in terms of
|
||||
|
@ -1708,186 +1710,9 @@ pub async fn run_tests(
|
|||
}
|
||||
|
||||
pub async fn run_tests_with_watch(
|
||||
cli_options: CliOptions,
|
||||
test_options: TestOptions,
|
||||
flags: Flags,
|
||||
test_flags: TestFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let factory = CliFactory::from_cli_options(Arc::new(cli_options));
|
||||
let cli_options = factory.cli_options();
|
||||
let module_graph_builder = factory.module_graph_builder().await?;
|
||||
let module_load_preparer = factory.module_load_preparer().await?;
|
||||
let file_fetcher = factory.file_fetcher()?;
|
||||
let file_watcher = factory.file_watcher()?;
|
||||
// Various test files should not share the same permissions in terms of
|
||||
// `PermissionsContainer` - otherwise granting/revoking permissions in one
|
||||
// file would have impact on other files, which is undesirable.
|
||||
let permissions =
|
||||
Permissions::from_options(&cli_options.permissions_options())?;
|
||||
let graph_kind = cli_options.type_check_mode().as_graph_kind();
|
||||
let log_level = cli_options.log_level();
|
||||
|
||||
let resolver = |changed: Option<Vec<PathBuf>>| {
|
||||
let paths_to_watch = test_options.files.include.clone();
|
||||
let paths_to_watch_clone = paths_to_watch.clone();
|
||||
let files_changed = changed.is_some();
|
||||
let test_options = &test_options;
|
||||
let cli_options = cli_options.clone();
|
||||
let module_graph_builder = module_graph_builder.clone();
|
||||
|
||||
async move {
|
||||
let test_modules = if test_options.doc {
|
||||
collect_specifiers(&test_options.files, is_supported_test_ext)
|
||||
} else {
|
||||
collect_specifiers(&test_options.files, is_supported_test_path)
|
||||
}?;
|
||||
|
||||
let mut paths_to_watch = paths_to_watch_clone;
|
||||
let mut modules_to_reload = if files_changed {
|
||||
Vec::new()
|
||||
} else {
|
||||
test_modules.clone()
|
||||
};
|
||||
let graph = module_graph_builder
|
||||
.create_graph(graph_kind, test_modules.clone())
|
||||
.await?;
|
||||
graph_valid_with_cli_options(&graph, &test_modules, &cli_options)?;
|
||||
|
||||
// TODO(@kitsonk) - This should be totally derivable from the graph.
|
||||
for specifier in test_modules {
|
||||
fn get_dependencies<'a>(
|
||||
graph: &'a deno_graph::ModuleGraph,
|
||||
maybe_module: Option<&'a deno_graph::Module>,
|
||||
// This needs to be accessible to skip getting dependencies if they're already there,
|
||||
// otherwise this will cause a stack overflow with circular dependencies
|
||||
output: &mut HashSet<&'a ModuleSpecifier>,
|
||||
) {
|
||||
if let Some(module) = maybe_module.and_then(|m| m.esm()) {
|
||||
for dep in module.dependencies.values() {
|
||||
if let Some(specifier) = &dep.get_code() {
|
||||
if !output.contains(specifier) {
|
||||
output.insert(specifier);
|
||||
get_dependencies(graph, graph.get(specifier), output);
|
||||
}
|
||||
}
|
||||
if let Some(specifier) = &dep.get_type() {
|
||||
if !output.contains(specifier) {
|
||||
output.insert(specifier);
|
||||
get_dependencies(graph, graph.get(specifier), output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This test module and all it's dependencies
|
||||
let mut modules = HashSet::new();
|
||||
modules.insert(&specifier);
|
||||
get_dependencies(&graph, graph.get(&specifier), &mut modules);
|
||||
|
||||
paths_to_watch.extend(
|
||||
modules
|
||||
.iter()
|
||||
.filter_map(|specifier| specifier.to_file_path().ok()),
|
||||
);
|
||||
|
||||
if let Some(changed) = &changed {
|
||||
for path in changed
|
||||
.iter()
|
||||
.filter_map(|path| ModuleSpecifier::from_file_path(path).ok())
|
||||
{
|
||||
if modules.contains(&path) {
|
||||
modules_to_reload.push(specifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((paths_to_watch, modules_to_reload))
|
||||
}
|
||||
.map(move |result| {
|
||||
if files_changed
|
||||
&& matches!(result, Ok((_, ref modules)) if modules.is_empty())
|
||||
{
|
||||
ResolutionResult::Ignore
|
||||
} else {
|
||||
match result {
|
||||
Ok((paths_to_watch, modules_to_reload)) => {
|
||||
ResolutionResult::Restart {
|
||||
paths_to_watch,
|
||||
result: Ok(modules_to_reload),
|
||||
}
|
||||
}
|
||||
Err(e) => ResolutionResult::Restart {
|
||||
paths_to_watch,
|
||||
result: Err(e),
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let create_cli_main_worker_factory =
|
||||
factory.create_cli_main_worker_factory_func().await?;
|
||||
let operation = |modules_to_reload: Vec<ModuleSpecifier>| {
|
||||
let permissions = &permissions;
|
||||
let test_options = &test_options;
|
||||
file_watcher.reset();
|
||||
let cli_options = cli_options.clone();
|
||||
let file_fetcher = file_fetcher.clone();
|
||||
let module_load_preparer = module_load_preparer.clone();
|
||||
let create_cli_main_worker_factory = create_cli_main_worker_factory.clone();
|
||||
|
||||
async move {
|
||||
let worker_factory = Arc::new(create_cli_main_worker_factory());
|
||||
let specifiers_with_mode = fetch_specifiers_with_test_mode(
|
||||
&file_fetcher,
|
||||
&test_options.files,
|
||||
&test_options.doc,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|(specifier, _)| modules_to_reload.contains(specifier))
|
||||
.collect::<Vec<(ModuleSpecifier, TestMode)>>();
|
||||
|
||||
check_specifiers(
|
||||
&cli_options,
|
||||
&file_fetcher,
|
||||
&module_load_preparer,
|
||||
specifiers_with_mode.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if test_options.no_run {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
test_specifiers(
|
||||
worker_factory,
|
||||
permissions,
|
||||
specifiers_with_mode
|
||||
.into_iter()
|
||||
.filter_map(|(s, m)| match m {
|
||||
TestMode::Documentation => None,
|
||||
_ => Some(s),
|
||||
})
|
||||
.collect(),
|
||||
TestSpecifiersOptions {
|
||||
concurrent_jobs: test_options.concurrent_jobs,
|
||||
fail_fast: test_options.fail_fast,
|
||||
log_level,
|
||||
specifier: TestSpecifierOptions {
|
||||
filter: TestFilter::from_flag(&test_options.filter),
|
||||
shuffle: test_options.shuffle,
|
||||
trace_ops: test_options.trace_ops,
|
||||
},
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
// On top of the sigint handlers which are added and unbound for each test
|
||||
// run, a process-scoped basic exit handler is required due to a tokio
|
||||
// limitation where it doesn't unbind its own handler for the entire process
|
||||
|
@ -1901,14 +1726,118 @@ pub async fn run_tests_with_watch(
|
|||
}
|
||||
});
|
||||
|
||||
let clear_screen = !cli_options.no_clear_screen();
|
||||
let clear_screen = !flags.no_clear_screen;
|
||||
file_watcher::watch_func(
|
||||
resolver,
|
||||
operation,
|
||||
flags,
|
||||
file_watcher::PrintConfig {
|
||||
job_name: "Test".to_string(),
|
||||
clear_screen,
|
||||
},
|
||||
move |flags, sender, changed_paths| {
|
||||
let test_flags = test_flags.clone();
|
||||
Ok(async move {
|
||||
let factory = CliFactoryBuilder::new()
|
||||
.with_watcher(sender.clone())
|
||||
.build_from_flags(flags)
|
||||
.await?;
|
||||
let cli_options = factory.cli_options();
|
||||
let test_options = cli_options.resolve_test_options(test_flags)?;
|
||||
|
||||
if let Some(watch_paths) = cli_options.watch_paths() {
|
||||
let _ = sender.send(watch_paths);
|
||||
}
|
||||
let _ = sender.send(test_options.files.include.clone());
|
||||
|
||||
let graph_kind = cli_options.type_check_mode().as_graph_kind();
|
||||
let log_level = cli_options.log_level();
|
||||
let cli_options = cli_options.clone();
|
||||
let module_graph_builder = factory.module_graph_builder().await?;
|
||||
let file_fetcher = factory.file_fetcher()?;
|
||||
let test_modules = if test_options.doc {
|
||||
collect_specifiers(&test_options.files, is_supported_test_ext)
|
||||
} else {
|
||||
collect_specifiers(&test_options.files, is_supported_test_path)
|
||||
}?;
|
||||
let permissions =
|
||||
Permissions::from_options(&cli_options.permissions_options())?;
|
||||
|
||||
let graph = module_graph_builder
|
||||
.create_graph(graph_kind, test_modules.clone())
|
||||
.await?;
|
||||
graph_valid_with_cli_options(&graph, &test_modules, &cli_options)?;
|
||||
|
||||
let test_modules_to_reload = if let Some(changed_paths) = changed_paths
|
||||
{
|
||||
let changed_specifiers = changed_paths
|
||||
.into_iter()
|
||||
.filter_map(|p| ModuleSpecifier::from_file_path(p).ok())
|
||||
.collect::<HashSet<_>>();
|
||||
let mut result = Vec::new();
|
||||
for test_module_specifier in test_modules {
|
||||
if has_graph_root_local_dependent_changed(
|
||||
&graph,
|
||||
&test_module_specifier,
|
||||
&changed_specifiers,
|
||||
) {
|
||||
result.push(test_module_specifier.clone());
|
||||
}
|
||||
}
|
||||
result
|
||||
} else {
|
||||
test_modules.clone()
|
||||
};
|
||||
|
||||
let worker_factory =
|
||||
Arc::new(factory.create_cli_main_worker_factory().await?);
|
||||
let module_load_preparer = factory.module_load_preparer().await?;
|
||||
let specifiers_with_mode = fetch_specifiers_with_test_mode(
|
||||
file_fetcher,
|
||||
&test_options.files,
|
||||
&test_options.doc,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|(specifier, _)| test_modules_to_reload.contains(specifier))
|
||||
.collect::<Vec<(ModuleSpecifier, TestMode)>>();
|
||||
|
||||
check_specifiers(
|
||||
&cli_options,
|
||||
file_fetcher,
|
||||
module_load_preparer,
|
||||
specifiers_with_mode.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if test_options.no_run {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
test_specifiers(
|
||||
worker_factory,
|
||||
&permissions,
|
||||
specifiers_with_mode
|
||||
.into_iter()
|
||||
.filter_map(|(s, m)| match m {
|
||||
TestMode::Documentation => None,
|
||||
_ => Some(s),
|
||||
})
|
||||
.collect(),
|
||||
TestSpecifiersOptions {
|
||||
concurrent_jobs: test_options.concurrent_jobs,
|
||||
fail_fast: test_options.fail_fast,
|
||||
log_level,
|
||||
specifier: TestSpecifierOptions {
|
||||
filter: TestFilter::from_flag(&test_options.filter),
|
||||
shuffle: test_options.shuffle,
|
||||
trace_ops: test_options.trace_ops,
|
||||
},
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue