0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-02-08 07:16:56 -05:00

single factory

This commit is contained in:
Nayeem Rahman 2025-01-20 08:15:23 +00:00
parent 9d9a88f7c3
commit 766452fca4
9 changed files with 445 additions and 539 deletions

View file

@ -8,7 +8,7 @@ mod lockfile;
mod package_json; mod package_json;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::BTreeSet; use std::collections::BTreeMap;
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
use std::net::SocketAddr; use std::net::SocketAddr;
@ -557,15 +557,6 @@ struct CliOptionOverrides {
import_map_specifier: Option<Option<ModuleSpecifier>>, import_map_specifier: Option<Option<ModuleSpecifier>>,
} }
/// Overrides for the options below that when set will
/// use these values over the values derived from the
/// CLI flags or config file.
#[derive(Debug, Clone)]
pub struct ScopeOptions {
pub scope: Option<Arc<ModuleSpecifier>>,
pub all_scopes: Arc<BTreeSet<Arc<ModuleSpecifier>>>,
}
fn load_external_import_map( fn load_external_import_map(
deno_json: &ConfigFile, deno_json: &ConfigFile,
) -> Result<Option<(PathBuf, serde_json::Value)>, AnyError> { ) -> Result<Option<(PathBuf, serde_json::Value)>, AnyError> {
@ -593,11 +584,10 @@ pub struct CliOptions {
npmrc: Arc<ResolvedNpmRc>, npmrc: Arc<ResolvedNpmRc>,
maybe_lockfile: Option<Arc<CliLockfile>>, maybe_lockfile: Option<Arc<CliLockfile>>,
maybe_external_import_map: Option<(PathBuf, serde_json::Value)>, maybe_external_import_map: Option<(PathBuf, serde_json::Value)>,
sys: CliSys,
overrides: CliOptionOverrides, overrides: CliOptionOverrides,
pub start_dir: Arc<WorkspaceDirectory>, pub start_dir: Arc<WorkspaceDirectory>,
pub all_dirs: BTreeMap<Arc<Url>, Arc<WorkspaceDirectory>>,
pub deno_dir_provider: Arc<DenoDirProvider>, pub deno_dir_provider: Arc<DenoDirProvider>,
pub scope_options: Option<Arc<ScopeOptions>>,
} }
impl CliOptions { impl CliOptions {
@ -611,7 +601,6 @@ impl CliOptions {
start_dir: Arc<WorkspaceDirectory>, start_dir: Arc<WorkspaceDirectory>,
force_global_cache: bool, force_global_cache: bool,
maybe_external_import_map: Option<(PathBuf, serde_json::Value)>, maybe_external_import_map: Option<(PathBuf, serde_json::Value)>,
scope_options: Option<Arc<ScopeOptions>>,
) -> Result<Self, AnyError> { ) -> Result<Self, AnyError> {
if let Some(insecure_allowlist) = if let Some(insecure_allowlist) =
flags.unsafely_ignore_certificate_errors.as_ref() flags.unsafely_ignore_certificate_errors.as_ref()
@ -643,6 +632,9 @@ impl CliOptions {
load_env_variables_from_env_file(flags.env_file.as_ref()); load_env_variables_from_env_file(flags.env_file.as_ref());
let all_dirs = [(start_dir.dir_url().clone(), start_dir.clone())]
.into_iter()
.collect();
Ok(Self { Ok(Self {
flags, flags,
initial_cwd, initial_cwd,
@ -653,9 +645,8 @@ impl CliOptions {
main_module_cell: std::sync::OnceLock::new(), main_module_cell: std::sync::OnceLock::new(),
maybe_external_import_map, maybe_external_import_map,
start_dir, start_dir,
all_dirs,
deno_dir_provider, deno_dir_provider,
sys: sys.clone(),
scope_options,
}) })
} }
@ -752,39 +743,18 @@ impl CliOptions {
Arc::new(start_dir), Arc::new(start_dir),
false, false,
external_import_map, external_import_map,
None,
) )
} }
pub fn with_new_start_dir_and_scope_options( pub fn with_all_dirs(
&self, self,
start_dir: Arc<WorkspaceDirectory>, all_dirs: impl IntoIterator<Item = Arc<WorkspaceDirectory>>,
scope_options: Option<ScopeOptions>, ) -> Self {
) -> Result<Self, AnyError> { let all_dirs = all_dirs
let (npmrc, _) = discover_npmrc_from_workspace(&start_dir.workspace)?; .into_iter()
let external_import_map = .map(|d| (d.dir_url().clone(), d))
if let Some(deno_json) = start_dir.workspace.root_deno_json() { .collect();
load_external_import_map(deno_json)? Self { all_dirs, ..self }
} else {
None
};
let lockfile = CliLockfile::discover(
&self.sys,
&self.flags,
&start_dir.workspace,
external_import_map.as_ref().map(|(_, v)| v),
)?;
Self::new(
&self.sys,
self.flags.clone(),
self.initial_cwd().to_path_buf(),
lockfile.map(Arc::new),
npmrc,
start_dir,
false,
external_import_map,
scope_options.map(Arc::new),
)
} }
/// This method is purposefully verbose to disourage its use. Do not use it /// This method is purposefully verbose to disourage its use. Do not use it

View file

@ -1,6 +1,5 @@
// Copyright 2018-2025 the Deno authors. MIT license. // Copyright 2018-2025 the Deno authors. MIT license.
use std::collections::BTreeSet;
use std::collections::HashSet; use std::collections::HashSet;
use std::future::Future; use std::future::Future;
use std::path::PathBuf; use std::path::PathBuf;
@ -13,7 +12,6 @@ use deno_config::glob::FilePatterns;
use deno_config::workspace::PackageJsonDepResolution; use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::WorkspaceDirectory; use deno_config::workspace::WorkspaceDirectory;
use deno_config::workspace::WorkspaceResolver; use deno_config::workspace::WorkspaceResolver;
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
use deno_core::FeatureChecker; use deno_core::FeatureChecker;
@ -42,7 +40,6 @@ use deno_runtime::deno_fs::RealFs;
use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker;
use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_permissions::PermissionsOptions;
use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::rustls::RootCertStore;
use deno_runtime::deno_tls::RootCertStoreProvider; use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::deno_web::BlobStore; use deno_runtime::deno_web::BlobStore;
@ -57,7 +54,6 @@ use crate::args::CliOptions;
use crate::args::DenoSubcommand; use crate::args::DenoSubcommand;
use crate::args::Flags; use crate::args::Flags;
use crate::args::NpmInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::args::ScopeOptions;
use crate::args::TsConfigType; use crate::args::TsConfigType;
use crate::cache::Caches; use crate::cache::Caches;
use crate::cache::CodeCache; use crate::cache::CodeCache;
@ -79,7 +75,6 @@ use crate::graph_util::ModuleGraphCreator;
use crate::http_util::HttpClientProvider; use crate::http_util::HttpClientProvider;
use crate::module_loader::CliModuleLoaderFactory; use crate::module_loader::CliModuleLoaderFactory;
use crate::module_loader::ModuleLoadPreparer; use crate::module_loader::ModuleLoadPreparer;
use crate::module_loader::PrepareModuleLoadError;
use crate::node::CliCjsCodeAnalyzer; use crate::node::CliCjsCodeAnalyzer;
use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeCodeTranslator;
use crate::node::CliNodeResolver; use crate::node::CliNodeResolver;
@ -105,12 +100,10 @@ use crate::resolver::CliSloppyImportsResolver;
use crate::resolver::FoundPackageJsonDepFlag; use crate::resolver::FoundPackageJsonDepFlag;
use crate::standalone::binary::DenoCompileBinaryWriter; use crate::standalone::binary::DenoCompileBinaryWriter;
use crate::sys::CliSys; use crate::sys::CliSys;
use crate::tools::check::CheckError;
use crate::tools::check::TypeChecker; use crate::tools::check::TypeChecker;
use crate::tools::coverage::CoverageCollector; use crate::tools::coverage::CoverageCollector;
use crate::tools::lint::LintRuleProvider; use crate::tools::lint::LintRuleProvider;
use crate::tools::run::hmr::HmrRunner; use crate::tools::run::hmr::HmrRunner;
use crate::tsc::Diagnostics;
use crate::tsc::TypeCheckingCjsTracker; use crate::tsc::TypeCheckingCjsTracker;
use crate::util::file_watcher::WatcherCommunicator; use crate::util::file_watcher::WatcherCommunicator;
use crate::util::fs::canonicalize_path; use crate::util::fs::canonicalize_path;
@ -1247,15 +1240,96 @@ pub struct SpecifierInfo {
pub check_doc: bool, pub check_doc: bool,
} }
pub struct WorkspaceDirFilesFactory { pub struct CliFactoryWithWorkspaceFiles {
pub inner: CliFactory,
pub cli_options: Arc<CliOptions>,
specifiers: Vec<(ModuleSpecifier, SpecifierInfo)>, specifiers: Vec<(ModuleSpecifier, SpecifierInfo)>,
doc_snippet_specifiers: Vec<ModuleSpecifier>, doc_snippet_specifiers: Vec<ModuleSpecifier>,
cli_options: Arc<CliOptions>, initial_cwd: PathBuf,
cli_factory: CliFactory, }
permissions_options: Deferred<PermissionsOptions>,
impl CliFactoryWithWorkspaceFiles {
#[allow(clippy::type_complexity)]
pub async fn from_workspace_dirs_with_files<T: Clone>(
mut workspace_dirs_with_files: Vec<(Arc<WorkspaceDirectory>, FilePatterns)>,
collect_specifiers: fn(
FilePatterns,
Arc<CliOptions>,
Arc<CliFileFetcher>,
T,
) -> std::pin::Pin<
Box<
dyn Future<
Output = Result<Vec<(ModuleSpecifier, SpecifierInfo)>, AnyError>,
>,
>,
>,
args: T,
extract_doc_files: Option<fn(File) -> Result<Vec<File>, AnyError>>,
cli_options: CliOptions,
watcher_communicator: Option<&Arc<WatcherCommunicator>>,
) -> Result<Self, AnyError> {
let cli_options =
Arc::new(cli_options.with_all_dirs(
workspace_dirs_with_files.iter().map(|(d, _)| d.clone()),
));
let mut factory = CliFactory::from_cli_options(cli_options.clone());
factory.watcher_communicator = watcher_communicator.cloned();
let initial_cwd = cli_options.initial_cwd().to_path_buf();
if let Some(watcher_communicator) = watcher_communicator {
let _ = watcher_communicator.watch_paths(cli_options.watch_paths());
}
workspace_dirs_with_files.sort_by_cached_key(|(d, _)| d.dir_url().clone());
let mut specifiers = Vec::new();
let mut doc_snippet_specifiers = Vec::new();
for (_, files) in workspace_dirs_with_files {
if let Some(watcher_communicator) = watcher_communicator {
let _ = watcher_communicator.watch_paths(
files
.include
.iter()
.flat_map(|set| set.base_paths())
.collect(),
);
}
let file_fetcher = factory.file_fetcher()?;
let dir_specifiers = collect_specifiers(
files,
cli_options.clone(),
file_fetcher.clone(),
args.clone(),
)
.await?;
if let Some(extract_doc_files) = extract_doc_files {
let root_permissions = factory.root_permissions_container()?;
for (s, _) in dir_specifiers.iter().filter(|(_, i)| i.check_doc) {
let file = file_fetcher.fetch(s, root_permissions).await?;
let snippet_files = extract_doc_files(file)?;
for snippet_file in snippet_files {
doc_snippet_specifiers.push(snippet_file.url.clone());
file_fetcher.insert_memory_files(snippet_file);
}
}
}
specifiers.extend(dir_specifiers);
}
Ok(Self {
inner: factory,
cli_options,
specifiers,
doc_snippet_specifiers,
initial_cwd,
})
}
pub fn initial_cwd(&self) -> &PathBuf {
&self.initial_cwd
}
pub fn found_specifiers(&self) -> bool {
!self.specifiers.is_empty()
} }
impl WorkspaceDirFilesFactory {
pub fn checked_specifiers(&self) -> impl Iterator<Item = &ModuleSpecifier> { pub fn checked_specifiers(&self) -> impl Iterator<Item = &ModuleSpecifier> {
self self
.specifiers .specifiers
@ -1268,23 +1342,20 @@ impl WorkspaceDirFilesFactory {
&self, &self,
canonicalized_dep_paths: &HashSet<PathBuf>, canonicalized_dep_paths: &HashSet<PathBuf>,
) -> Result<Vec<&ModuleSpecifier>, AnyError> { ) -> Result<Vec<&ModuleSpecifier>, AnyError> {
let graph_kind = self let graph_kind =
.cli_factory self.inner.cli_options()?.type_check_mode().as_graph_kind();
.cli_options()? let module_graph_creator = self.inner.module_graph_creator().await?;
.type_check_mode() let specifiers = self.checked_specifiers().collect::<Vec<_>>();
.as_graph_kind();
let module_graph_creator = self.cli_factory.module_graph_creator().await?;
let specifiers = self.checked_specifiers().cloned().collect::<Vec<_>>();
let graph = module_graph_creator let graph = module_graph_creator
.create_graph( .create_graph(
graph_kind, graph_kind,
specifiers.clone(), specifiers.iter().map(|&s| s.clone()).collect(),
crate::graph_util::NpmCachingStrategy::Eager, crate::graph_util::NpmCachingStrategy::Eager,
) )
.await?; .await?;
module_graph_creator.graph_valid(&graph)?; module_graph_creator.graph_valid(&graph)?;
let dependent_specifiers = self let dependent_specifiers = specifiers
.checked_specifiers() .into_iter()
.filter(|s| { .filter(|s| {
let mut dependency_specifiers = graph.walk( let mut dependency_specifiers = graph.walk(
std::iter::once(*s), std::iter::once(*s),
@ -1313,172 +1384,16 @@ impl WorkspaceDirFilesFactory {
Ok(dependent_specifiers) Ok(dependent_specifiers)
} }
pub fn permissions_options(&self) -> &PermissionsOptions {
self
.permissions_options
.get_or_init(|| self.cli_options.permissions_options())
}
pub fn permission_desc_parser(
&self,
) -> Result<&Arc<RuntimePermissionDescriptorParser<CliSys>>, AnyError> {
self.cli_factory.permission_desc_parser()
}
pub async fn create_cli_main_worker_factory(
&self,
) -> Result<CliMainWorkerFactory, AnyError> {
self.cli_factory.create_cli_main_worker_factory().await
}
}
pub struct WorkspaceFilesFactory {
dirs: Vec<WorkspaceDirFilesFactory>,
initial_cwd: PathBuf,
}
impl WorkspaceFilesFactory {
#[allow(clippy::type_complexity)]
pub async fn from_workspace_dirs_with_files<T: Clone>(
mut workspace_dirs_with_files: Vec<(Arc<WorkspaceDirectory>, FilePatterns)>,
collect_specifiers: fn(
FilePatterns,
Arc<CliOptions>,
Arc<CliFileFetcher>,
T,
) -> std::pin::Pin<
Box<
dyn Future<
Output = Result<Vec<(ModuleSpecifier, SpecifierInfo)>, AnyError>,
>,
>,
>,
args: T,
extract_doc_files: Option<fn(File) -> Result<Vec<File>, AnyError>>,
cli_options: &CliOptions,
watcher_communicator: Option<&Arc<WatcherCommunicator>>,
) -> Result<Self, AnyError> {
let initial_cwd = cli_options.initial_cwd().to_path_buf();
if let Some(watcher_communicator) = watcher_communicator {
let _ = watcher_communicator.watch_paths(cli_options.watch_paths());
}
workspace_dirs_with_files.sort_by_cached_key(|(d, _)| d.dir_url().clone());
let all_scopes = Arc::new(
workspace_dirs_with_files
.iter()
.filter(|(d, _)| d.has_deno_or_pkg_json())
.map(|(d, _)| d.dir_url().clone())
.collect::<BTreeSet<_>>(),
);
let dir_count = workspace_dirs_with_files.len();
let mut dirs = Vec::with_capacity(dir_count);
for (workspace_dir, files) in workspace_dirs_with_files {
if let Some(watcher_communicator) = watcher_communicator {
let _ = watcher_communicator.watch_paths(
files
.include
.iter()
.flat_map(|set| set.base_paths())
.collect(),
);
}
let scope_options = (dir_count > 1).then(|| ScopeOptions {
scope: workspace_dir
.has_deno_or_pkg_json()
.then(|| workspace_dir.dir_url().clone()),
all_scopes: all_scopes.clone(),
});
let cli_options = Arc::new(
cli_options
.with_new_start_dir_and_scope_options(workspace_dir, scope_options)?,
);
let mut factory = CliFactory::from_cli_options(cli_options.clone());
factory.watcher_communicator = watcher_communicator.cloned();
let file_fetcher = factory.file_fetcher()?;
let specifiers = collect_specifiers(
files,
cli_options.clone(),
file_fetcher.clone(),
args.clone(),
)
.await?;
let mut doc_snippet_specifiers = vec![];
if let Some(extract_doc_files) = extract_doc_files {
let root_permissions = factory.root_permissions_container()?;
for (s, _) in specifiers.iter().filter(|(_, i)| i.check_doc) {
let file = file_fetcher.fetch(s, root_permissions).await?;
let snippet_files = extract_doc_files(file)?;
for snippet_file in snippet_files {
doc_snippet_specifiers.push(snippet_file.url.clone());
file_fetcher.insert_memory_files(snippet_file);
}
}
}
dirs.push(WorkspaceDirFilesFactory {
specifiers,
doc_snippet_specifiers,
cli_options,
cli_factory: factory,
permissions_options: Default::default(),
});
}
Ok(Self { dirs, initial_cwd })
}
pub fn dirs(&self) -> &Vec<WorkspaceDirFilesFactory> {
&self.dirs
}
pub fn initial_cwd(&self) -> &PathBuf {
&self.initial_cwd
}
pub fn found_specifiers(&self) -> bool {
self.dirs.iter().any(|e| !e.specifiers.is_empty())
}
pub async fn check(&self) -> Result<(), AnyError> { pub async fn check(&self) -> Result<(), AnyError> {
let mut diagnostics = vec![]; let main_graph_container =
let mut all_errors = vec![]; self.inner.main_module_graph_container().await?.clone();
for entry in &self.dirs { let specifiers = self.checked_specifiers().cloned().collect::<Vec<_>>();
let main_graph_container = entry if specifiers.is_empty() {
.cli_factory return Ok(());
.main_module_graph_container()
.await?
.clone();
let specifiers_for_typecheck =
entry.checked_specifiers().cloned().collect::<Vec<_>>();
if specifiers_for_typecheck.is_empty() {
continue;
} }
let ext_flag = entry.cli_factory.cli_options()?.ext_flag().as_ref(); let ext_flag = self.cli_options.ext_flag().as_ref();
if let Err(err) = main_graph_container main_graph_container
.check_specifiers(&specifiers_for_typecheck, ext_flag) .check_specifiers(&specifiers, ext_flag)
.await .await
{
match err {
PrepareModuleLoadError::Check(CheckError::Diagnostics(
Diagnostics(d),
)) => diagnostics.extend(d),
err => all_errors.push(err),
}
}
}
if !diagnostics.is_empty() {
all_errors.push(PrepareModuleLoadError::Check(CheckError::Diagnostics(
Diagnostics(diagnostics),
)));
}
if !all_errors.is_empty() {
return Err(anyhow!(
"{}",
all_errors
.into_iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join("\n\n"),
));
}
Ok(())
} }
} }

View file

@ -13,7 +13,6 @@ use deno_runtime::deno_permissions::PermissionsContainer;
use crate::args::CliOptions; use crate::args::CliOptions;
use crate::module_loader::ModuleLoadPreparer; use crate::module_loader::ModuleLoadPreparer;
use crate::module_loader::PrepareModuleLoadError;
use crate::util::fs::collect_specifiers; use crate::util::fs::collect_specifiers;
use crate::util::path::is_script_ext; use crate::util::path::is_script_ext;
@ -70,7 +69,7 @@ impl MainModuleGraphContainer {
&self, &self,
specifiers: &[ModuleSpecifier], specifiers: &[ModuleSpecifier],
ext_overwrite: Option<&String>, ext_overwrite: Option<&String>,
) -> Result<(), PrepareModuleLoadError> { ) -> Result<(), AnyError> {
let mut graph_permit = self.acquire_update_permit().await; let mut graph_permit = self.acquire_update_permit().await;
let graph = graph_permit.graph_mut(); let graph = graph_permit.graph_mut();
self self
@ -100,7 +99,7 @@ impl MainModuleGraphContainer {
log::warn!("{} No matching files found.", colors::yellow("Warning")); log::warn!("{} No matching files found.", colors::yellow("Warning"));
} }
Ok(self.check_specifiers(&specifiers, None).await?) self.check_specifiers(&specifiers, None).await
} }
pub fn collect_specifiers( pub fn collect_specifiers(

View file

@ -3666,7 +3666,6 @@ impl Inner {
workspace, workspace,
force_global_cache, force_global_cache,
None, None,
None,
)?; )?;
let open_docs = self.documents.documents(DocumentsFilter::OpenDiagnosable); let open_docs = self.documents.documents(DocumentsFilter::OpenDiagnosable);

View file

@ -39,8 +39,8 @@ use crate::args::CliOptions;
use crate::args::Flags; use crate::args::Flags;
use crate::colors; use crate::colors;
use crate::display::write_json_to_stdout; use crate::display::write_json_to_stdout;
use crate::factory::CliFactoryWithWorkspaceFiles;
use crate::factory::SpecifierInfo; use crate::factory::SpecifierInfo;
use crate::factory::WorkspaceFilesFactory;
use crate::ops; use crate::ops;
use crate::sys::CliSys; use crate::sys::CliSys;
use crate::tools::test::format_test_error; use crate::tools::test::format_test_error;
@ -284,42 +284,32 @@ async fn bench_specifier_inner(
/// Test a collection of specifiers with test modes concurrently. /// Test a collection of specifiers with test modes concurrently.
async fn bench_specifiers( async fn bench_specifiers(
workspace_files_factory: &WorkspaceFilesFactory, factory: &CliFactoryWithWorkspaceFiles,
changed_paths: Option<&HashSet<PathBuf>>, changed_paths: Option<&HashSet<PathBuf>>,
options: BenchSpecifierOptions, options: BenchSpecifierOptions,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let mut specifiers_with_services = vec![];
for factory in workspace_files_factory.dirs() {
let worker_factory =
Arc::new(factory.create_cli_main_worker_factory().await?);
let permission_desc_parser = factory.permission_desc_parser()?;
let permissions = Arc::new(Permissions::from_options(
permission_desc_parser.as_ref(),
factory.permissions_options(),
)?);
let specifiers = if let Some(changed_paths) = changed_paths { let specifiers = if let Some(changed_paths) = changed_paths {
factory.dependent_checked_specifiers(changed_paths).await? factory.dependent_checked_specifiers(changed_paths).await?
} else { } else {
factory.checked_specifiers().collect() factory.checked_specifiers().collect()
}; };
specifiers_with_services.extend(specifiers.into_iter().map(|s| { let worker_factory =
( Arc::new(factory.inner.create_cli_main_worker_factory().await?);
s.clone(), let permission_desc_parser = factory.inner.permission_desc_parser()?;
worker_factory.clone(), let permissions = Permissions::from_options(
permission_desc_parser.clone(), permission_desc_parser.as_ref(),
permissions.clone(), &factory.cli_options.permissions_options(),
) )?;
}));
}
let (sender, mut receiver) = unbounded_channel::<BenchEvent>(); let (sender, mut receiver) = unbounded_channel::<BenchEvent>();
let log_level = options.log_level; let log_level = options.log_level;
let option_for_handles = options.clone(); let option_for_handles = options.clone();
let join_handles = specifiers_with_services.into_iter().map( let join_handles = specifiers.into_iter().cloned().map(move |specifier| {
move |(specifier, worker_factory, permissions_desc_parser, permissions)| { let worker_factory = worker_factory.clone();
let permissions_container = PermissionsContainer::new( let permissions_container = PermissionsContainer::new(
permissions_desc_parser.clone(), permission_desc_parser.clone(),
permissions.as_ref().clone(), permissions.clone(),
); );
let sender = sender.clone(); let sender = sender.clone();
let options = option_for_handles.clone(); let options = option_for_handles.clone();
@ -333,8 +323,7 @@ async fn bench_specifiers(
); );
create_and_run_current_thread(future) create_and_run_current_thread(future)
}) })
}, });
);
let join_stream = stream::iter(join_handles) let join_stream = stream::iter(join_handles)
.buffer_unordered(1) .buffer_unordered(1)
@ -457,9 +446,8 @@ pub async fn run_benchmarks(
.resolve_bench_options_for_members(&bench_flags)? .resolve_bench_options_for_members(&bench_flags)?
.into_iter() .into_iter()
.map(|(d, o)| (d, o.files)) .map(|(d, o)| (d, o.files))
.collect(); .collect::<Vec<_>>();
let workspace_files_factory = let factory = CliFactoryWithWorkspaceFiles::from_workspace_dirs_with_files(
WorkspaceFilesFactory::from_workspace_dirs_with_files(
workspace_dirs_with_files, workspace_dirs_with_files,
|patterns, cli_options, _, _| { |patterns, cli_options, _, _| {
async move { async move {
@ -478,22 +466,22 @@ pub async fn run_benchmarks(
}, },
(), (),
None, None,
&cli_options, cli_options,
None, None,
) )
.await?; .await?;
if !workspace_files_factory.found_specifiers() { if !factory.found_specifiers() {
return Err(anyhow!("No test modules found")); return Err(anyhow!("No test modules found"));
} }
workspace_files_factory.check().await?; factory.check().await?;
if bench_flags.no_run { if bench_flags.no_run {
return Ok(()); return Ok(());
} }
bench_specifiers( bench_specifiers(
&workspace_files_factory, &factory,
None, None,
BenchSpecifierOptions { BenchSpecifierOptions {
filter: TestFilter::from_flag(&bench_flags.filter), filter: TestFilter::from_flag(&bench_flags.filter),
@ -531,8 +519,8 @@ pub async fn run_benchmarks_with_watch(
.into_iter() .into_iter()
.map(|(d, o)| (d, o.files)) .map(|(d, o)| (d, o.files))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let workspace_files_factory = let factory =
WorkspaceFilesFactory::from_workspace_dirs_with_files( CliFactoryWithWorkspaceFiles::from_workspace_dirs_with_files(
workspace_dirs_with_files, workspace_dirs_with_files,
|patterns, cli_options, _, _| { |patterns, cli_options, _, _| {
async move { async move {
@ -551,19 +539,19 @@ pub async fn run_benchmarks_with_watch(
}, },
(), (),
None, None,
&cli_options, cli_options,
Some(&watcher_communicator), Some(&watcher_communicator),
) )
.await?; .await?;
workspace_files_factory.check().await?; factory.check().await?;
if bench_flags.no_run { if bench_flags.no_run {
return Ok(()); return Ok(());
} }
bench_specifiers( bench_specifiers(
&workspace_files_factory, &factory,
changed_paths.map(|p| p.into_iter().collect()).as_ref(), changed_paths.map(|p| p.into_iter().collect()).as_ref(),
BenchSpecifierOptions { BenchSpecifierOptions {
filter: TestFilter::from_flag(&bench_flags.filter), filter: TestFilter::from_flag(&bench_flags.filter),

View file

@ -1,5 +1,6 @@
// Copyright 2018-2025 the Deno authors. MIT license. // Copyright 2018-2025 the Deno authors. MIT license.
use std::collections::BTreeMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::sync::Arc; use std::sync::Arc;
@ -7,6 +8,7 @@ use std::sync::Arc;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_config::deno_json; use deno_config::deno_json;
use deno_config::workspace::WorkspaceDirectory;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
use deno_error::JsErrorBox; use deno_error::JsErrorBox;
@ -15,6 +17,7 @@ use deno_graph::ModuleError;
use deno_graph::ModuleGraph; use deno_graph::ModuleGraph;
use deno_graph::ModuleLoadError; use deno_graph::ModuleLoadError;
use deno_lib::util::hash::FastInsecureHasher; use deno_lib::util::hash::FastInsecureHasher;
use deno_path_util::url_from_directory_path;
use deno_semver::npm::NpmPackageNvReference; use deno_semver::npm::NpmPackageNvReference;
use deno_terminal::colors; use deno_terminal::colors;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@ -32,8 +35,8 @@ use crate::args::TypeCheckMode;
use crate::cache::CacheDBHash; use crate::cache::CacheDBHash;
use crate::cache::Caches; use crate::cache::Caches;
use crate::cache::TypeCheckCache; use crate::cache::TypeCheckCache;
use crate::factory::CliFactoryWithWorkspaceFiles;
use crate::factory::SpecifierInfo; use crate::factory::SpecifierInfo;
use crate::factory::WorkspaceFilesFactory;
use crate::graph_util::maybe_additional_sloppy_imports_message; use crate::graph_util::maybe_additional_sloppy_imports_message;
use crate::graph_util::BuildFastCheckGraphOptions; use crate::graph_util::BuildFastCheckGraphOptions;
use crate::graph_util::ModuleGraphBuilder; use crate::graph_util::ModuleGraphBuilder;
@ -59,8 +62,7 @@ pub async fn check(
ignore: Default::default(), ignore: Default::default(),
include: check_flags.files, include: check_flags.files,
})?; })?;
let workspace_files_factory = let factory = CliFactoryWithWorkspaceFiles::from_workspace_dirs_with_files(
WorkspaceFilesFactory::from_workspace_dirs_with_files(
workspace_dirs_with_files, workspace_dirs_with_files,
|patterns, cli_options, _, (doc, doc_only)| { |patterns, cli_options, _, (doc, doc_only)| {
async move { async move {
@ -79,14 +81,14 @@ pub async fn check(
}, },
(check_flags.doc, check_flags.doc_only), (check_flags.doc, check_flags.doc_only),
Some(extract_snippet_files), Some(extract_snippet_files),
&cli_options, cli_options,
None, None,
) )
.await?; .await?;
if !workspace_files_factory.found_specifiers() { if !factory.found_specifiers() {
log::warn!("{} No matching files found.", colors::yellow("Warning")); log::warn!("{} No matching files found.", colors::yellow("Warning"));
} }
workspace_files_factory.check().await factory.check().await
} }
/// Options for performing a check of a module graph. Note that the decision to /// Options for performing a check of a module graph. Note that the decision to
@ -229,17 +231,6 @@ impl TypeChecker {
} }
log::debug!("Type checking."); log::debug!("Type checking.");
let ts_config_result = self
.cli_options
.resolve_ts_config_for_emit(TsConfigType::Check { lib: options.lib })?;
if options.log_ignored_options {
check_warn_tsconfig(&ts_config_result);
}
let type_check_mode = options.type_check_mode;
let ts_config = ts_config_result.ts_config;
let cache = TypeCheckCache::new(self.caches.type_checking_cache_db());
let check_js = ts_config.get_check_js();
// add fast check to the graph before getting the roots // add fast check to the graph before getting the roots
if options.build_fast_check_graph { if options.build_fast_check_graph {
@ -251,20 +242,45 @@ impl TypeChecker {
)?; )?;
} }
let graph = Arc::new(graph);
let mut all_dirs = self.cli_options.all_dirs.clone();
let initial_cwd_url =
url_from_directory_path(self.cli_options.initial_cwd())
.map_err(JsErrorBox::from_err)?;
let initial_workspace_dir_url = all_dirs
.keys()
.rfind(|s| initial_cwd_url.as_str().starts_with(s.as_str()))
.cloned()
.unwrap_or_else(|| {
all_dirs.insert(
self.cli_options.start_dir.dir_url().clone(),
self.cli_options.start_dir.clone(),
);
self.cli_options.start_dir.dir_url().clone()
});
let is_scoped = all_dirs.len() > 1;
let mut diagnostics = Diagnostics::default();
for (dir_url, workspace_dir) in &all_dirs {
let is_initial_workspace_dir = *dir_url == initial_workspace_dir_url;
let ts_config_result = workspace_dir
.to_ts_config_for_emit(TsConfigType::Check { lib: options.lib })?;
if options.log_ignored_options {
check_warn_tsconfig(&ts_config_result);
}
let type_check_mode = options.type_check_mode;
let ts_config = ts_config_result.ts_config;
let cache = TypeCheckCache::new(self.caches.type_checking_cache_db());
let check_js = ts_config.get_check_js();
let is_visible_diagnostic = |d: &tsc::Diagnostic| { let is_visible_diagnostic = |d: &tsc::Diagnostic| {
if self.is_remote_diagnostic(d) { if self.is_remote_diagnostic(d) {
return type_check_mode == TypeCheckMode::All return type_check_mode == TypeCheckMode::All
&& d.include_when_remote() && d.include_when_remote()
&& self && !is_scoped;
.cli_options
.scope_options
.as_ref()
.map(|o| o.scope.is_none())
.unwrap_or(true);
} }
let Some(scope_options) = &self.cli_options.scope_options else {
return true;
};
let Some(specifier) = d let Some(specifier) = d
.file_name .file_name
.as_ref() .as_ref()
@ -275,14 +291,16 @@ impl TypeChecker {
if specifier.scheme() != "file" { if specifier.scheme() != "file" {
return true; return true;
} }
let scope = scope_options let scope = all_dirs
.all_scopes .keys()
.iter()
.rfind(|s| specifier.as_str().starts_with(s.as_str())); .rfind(|s| specifier.as_str().starts_with(s.as_str()));
scope == scope_options.scope.as_ref() scope
.map(|s| s == dir_url)
.unwrap_or(is_initial_workspace_dir)
}; };
let TscRoots { let TscRoots {
roots: root_names, roots: root_names,
display_roots,
missing_diagnostics, missing_diagnostics,
maybe_check_hash, maybe_check_hash,
} = get_tsc_roots( } = get_tsc_roots(
@ -294,24 +312,31 @@ impl TypeChecker {
check_state_hash(&self.npm_resolver), check_state_hash(&self.npm_resolver),
type_check_mode, type_check_mode,
&ts_config, &ts_config,
&all_dirs,
&initial_workspace_dir_url,
is_scoped.then_some(dir_url.as_ref()),
); );
let missing_diagnostics = missing_diagnostics.filter(is_visible_diagnostic); let missing_diagnostics =
missing_diagnostics.filter(is_visible_diagnostic);
let has_missing_diagnostics = !missing_diagnostics.is_empty();
diagnostics.extend(missing_diagnostics);
if root_names.is_empty() && missing_diagnostics.is_empty() { if root_names.is_empty() {
return Ok((graph.into(), Default::default())); continue;
} }
if !options.reload { if !options.reload {
// do not type check if we know this is type checked // do not type check if we know this is type checked
if let Some(check_hash) = maybe_check_hash { if let Some(check_hash) = maybe_check_hash {
if cache.has_check_hash(check_hash) { if cache.has_check_hash(check_hash) {
log::debug!("Already type checked."); log::debug!("Already type checked.");
return Ok((graph.into(), Default::default())); continue;
} }
} }
} }
for root in &graph.roots { for root in &display_roots {
let root_str = root.as_str(); let root_str = root.as_str();
log::info!( log::info!(
"{} {}", "{} {}",
@ -323,10 +348,11 @@ impl TypeChecker {
// while there might be multiple roots, we can't "merge" the build info, so we // while there might be multiple roots, we can't "merge" the build info, so we
// try to retrieve the build info for first root, which is the most common use // try to retrieve the build info for first root, which is the most common use
// case. // case.
let first_root = root_names[0].0.clone();
let maybe_tsbuildinfo = if options.reload { let maybe_tsbuildinfo = if options.reload {
None None
} else { } else {
cache.get_tsbuildinfo(&graph.roots[0]) cache.get_tsbuildinfo(&first_root)
}; };
// to make tsc build info work, we need to consistently hash modules, so that // to make tsc build info work, we need to consistently hash modules, so that
// tsc can better determine if an emit is still valid or not, so we provide // tsc can better determine if an emit is still valid or not, so we provide
@ -334,7 +360,6 @@ impl TypeChecker {
let tsconfig_hash_data = FastInsecureHasher::new_deno_versioned() let tsconfig_hash_data = FastInsecureHasher::new_deno_versioned()
.write(&ts_config.as_bytes()) .write(&ts_config.as_bytes())
.finish(); .finish();
let graph = Arc::new(graph);
let response = tsc::exec(tsc::Request { let response = tsc::exec(tsc::Request {
config: ts_config, config: ts_config,
debug: self.cli_options.log_level() == Some(log::Level::Debug), debug: self.cli_options.log_level() == Some(log::Level::Debug),
@ -353,22 +378,22 @@ impl TypeChecker {
let response_diagnostics = let response_diagnostics =
response.diagnostics.filter(is_visible_diagnostic); response.diagnostics.filter(is_visible_diagnostic);
let mut diagnostics = missing_diagnostics;
diagnostics.extend(response_diagnostics);
diagnostics.apply_fast_check_source_maps(&graph);
if let Some(tsbuildinfo) = response.maybe_tsbuildinfo { if let Some(tsbuildinfo) = response.maybe_tsbuildinfo {
cache.set_tsbuildinfo(&graph.roots[0], &tsbuildinfo); cache.set_tsbuildinfo(&first_root, &tsbuildinfo);
} }
if diagnostics.is_empty() { if !has_missing_diagnostics && response_diagnostics.is_empty() {
if let Some(check_hash) = maybe_check_hash { if let Some(check_hash) = maybe_check_hash {
cache.add_check_hash(check_hash); cache.add_check_hash(check_hash);
} }
} }
diagnostics.extend(response_diagnostics);
log::debug!("{}", response.stats); log::debug!("{}", response.stats);
}
diagnostics.apply_fast_check_source_maps(&graph);
Ok((graph, diagnostics)) Ok((graph, diagnostics))
} }
@ -390,6 +415,7 @@ impl TypeChecker {
struct TscRoots { struct TscRoots {
roots: Vec<(ModuleSpecifier, MediaType)>, roots: Vec<(ModuleSpecifier, MediaType)>,
display_roots: Vec<ModuleSpecifier>,
missing_diagnostics: tsc::Diagnostics, missing_diagnostics: tsc::Diagnostics,
maybe_check_hash: Option<CacheDBHash>, maybe_check_hash: Option<CacheDBHash>,
} }
@ -410,6 +436,9 @@ fn get_tsc_roots(
npm_cache_state_hash: Option<u64>, npm_cache_state_hash: Option<u64>,
type_check_mode: TypeCheckMode, type_check_mode: TypeCheckMode,
ts_config: &TsConfig, ts_config: &TsConfig,
all_dirs: &BTreeMap<Arc<ModuleSpecifier>, Arc<WorkspaceDirectory>>,
initial_workspace_dir_url: &ModuleSpecifier,
current_workspace_dir_url: Option<&ModuleSpecifier>,
) -> TscRoots { ) -> TscRoots {
fn maybe_get_check_entry( fn maybe_get_check_entry(
module: &deno_graph::Module, module: &deno_graph::Module,
@ -495,6 +524,7 @@ fn get_tsc_roots(
let mut result = TscRoots { let mut result = TscRoots {
roots: Vec::with_capacity(graph.specifiers_count()), roots: Vec::with_capacity(graph.specifiers_count()),
display_roots: Vec::with_capacity(graph.roots.len()),
missing_diagnostics: Default::default(), missing_diagnostics: Default::default(),
maybe_check_hash: None, maybe_check_hash: None,
}; };
@ -525,6 +555,16 @@ fn get_tsc_roots(
// put in the global types first so that they're resolved before anything else // put in the global types first so that they're resolved before anything else
for (referrer, import) in graph.imports.iter() { for (referrer, import) in graph.imports.iter() {
if let Some(current_workspace_dir_url) = current_workspace_dir_url {
let scope = all_dirs
.keys()
.rfind(|s| referrer.as_str().starts_with(s.as_str()))
.map(|s| s.as_ref())
.unwrap_or(initial_workspace_dir_url);
if scope != current_workspace_dir_url {
continue;
}
}
for specifier in import for specifier in import
.dependencies .dependencies
.values() .values()
@ -556,6 +596,17 @@ fn get_tsc_roots(
// then the roots // then the roots
for root in &graph.roots { for root in &graph.roots {
if let Some(current_workspace_dir_url) = current_workspace_dir_url {
let scope = all_dirs
.keys()
.rfind(|s| root.as_str().starts_with(s.as_str()))
.map(|s| s.as_ref())
.unwrap_or(initial_workspace_dir_url);
if scope != current_workspace_dir_url {
continue;
}
}
result.display_roots.push(root.clone());
let specifier = graph.resolve(root); let specifier = graph.resolve(root);
if seen.insert(specifier) { if seen.insert(specifier) {
pending.push_back((specifier, false)); pending.push_back((specifier, false));

View file

@ -73,8 +73,8 @@ use crate::args::TestFlags;
use crate::args::TestReporterConfig; use crate::args::TestReporterConfig;
use crate::colors; use crate::colors;
use crate::display; use crate::display;
use crate::factory::CliFactoryWithWorkspaceFiles;
use crate::factory::SpecifierInfo; use crate::factory::SpecifierInfo;
use crate::factory::WorkspaceFilesFactory;
use crate::file_fetcher::CliFileFetcher; use crate::file_fetcher::CliFileFetcher;
use crate::ops; use crate::ops;
use crate::sys::CliSys; use crate::sys::CliSys;
@ -1180,38 +1180,27 @@ static HAS_TEST_RUN_SIGINT_HANDLER: AtomicBool = AtomicBool::new(false);
/// Test a collection of specifiers with test modes concurrently. /// Test a collection of specifiers with test modes concurrently.
async fn test_specifiers( async fn test_specifiers(
workspace_files_factory: &WorkspaceFilesFactory, factory: &CliFactoryWithWorkspaceFiles,
changed_paths: Option<&HashSet<PathBuf>>, changed_paths: Option<&HashSet<PathBuf>>,
options: TestSpecifiersOptions, options: TestSpecifiersOptions,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let mut specifiers_with_services = vec![]; let mut specifiers = if let Some(changed_paths) = changed_paths {
for factory in workspace_files_factory.dirs() {
let worker_factory =
Arc::new(factory.create_cli_main_worker_factory().await?);
let permission_desc_parser = factory.permission_desc_parser()?;
let permissions = Arc::new(Permissions::from_options(
permission_desc_parser.as_ref(),
factory.permissions_options(),
)?);
let specifiers = if let Some(changed_paths) = changed_paths {
factory.dependent_checked_specifiers(changed_paths).await? factory.dependent_checked_specifiers(changed_paths).await?
} else { } else {
factory.checked_specifiers().collect() factory.checked_specifiers().collect()
}; };
specifiers_with_services.extend(specifiers.into_iter().map(|s| {
(
s.clone(),
worker_factory.clone(),
permission_desc_parser.clone(),
permissions.clone(),
)
}));
}
if let Some(seed) = options.specifier.shuffle { if let Some(seed) = options.specifier.shuffle {
let mut rng = SmallRng::seed_from_u64(seed); let mut rng = SmallRng::seed_from_u64(seed);
specifiers_with_services.sort_by_cached_key(|(s, ..)| s.to_string()); specifiers.sort();
specifiers_with_services.shuffle(&mut rng); specifiers.shuffle(&mut rng);
} }
let worker_factory =
Arc::new(factory.inner.create_cli_main_worker_factory().await?);
let permission_desc_parser = factory.inner.permission_desc_parser()?;
let permissions = Permissions::from_options(
permission_desc_parser.as_ref(),
&factory.cli_options.permissions_options(),
)?;
let (test_event_sender_factory, receiver) = create_test_event_channel(); let (test_event_sender_factory, receiver) = create_test_event_channel();
let concurrent_jobs = options.concurrent_jobs; let concurrent_jobs = options.concurrent_jobs;
@ -1225,11 +1214,11 @@ async fn test_specifiers(
let reporter = get_test_reporter(&options); let reporter = get_test_reporter(&options);
let fail_fast_tracker = FailFastTracker::new(options.fail_fast); let fail_fast_tracker = FailFastTracker::new(options.fail_fast);
let join_handles = specifiers_with_services.into_iter().map( let join_handles = specifiers.into_iter().cloned().map(move |specifier| {
move |(specifier, worker_factory, permission_desc_parser, permissions)| { let worker_factory = worker_factory.clone();
let permissions_container = PermissionsContainer::new( let permissions_container = PermissionsContainer::new(
permission_desc_parser, permission_desc_parser.clone(),
permissions.as_ref().clone(), permissions.clone(),
); );
let worker_sender = test_event_sender_factory.worker(); let worker_sender = test_event_sender_factory.worker();
let fail_fast_tracker = fail_fast_tracker.clone(); let fail_fast_tracker = fail_fast_tracker.clone();
@ -1244,8 +1233,7 @@ async fn test_specifiers(
specifier_options, specifier_options,
)) ))
}) })
}, });
);
let join_stream = stream::iter(join_handles) let join_stream = stream::iter(join_handles)
.buffer_unordered(concurrent_jobs.get()) .buffer_unordered(concurrent_jobs.get())
@ -1516,9 +1504,8 @@ pub async fn run_tests(
.resolve_test_options_for_members(&test_flags)? .resolve_test_options_for_members(&test_flags)?
.into_iter() .into_iter()
.map(|(d, o)| (d, o.files)) .map(|(d, o)| (d, o.files))
.collect(); .collect::<Vec<_>>();
let workspace_files_factory = let factory = CliFactoryWithWorkspaceFiles::from_workspace_dirs_with_files(
WorkspaceFilesFactory::from_workspace_dirs_with_files(
workspace_dirs_with_files, workspace_dirs_with_files,
|patterns, cli_options, file_fetcher, doc| { |patterns, cli_options, file_fetcher, doc| {
collect_specifiers_for_tests(patterns, cli_options, file_fetcher, doc) collect_specifiers_for_tests(patterns, cli_options, file_fetcher, doc)
@ -1526,30 +1513,29 @@ pub async fn run_tests(
}, },
test_flags.doc, test_flags.doc,
Some(extract_doc_tests), Some(extract_doc_tests),
&cli_options, cli_options,
None, None,
) )
.await?; .await?;
if !test_flags.permit_no_files && !workspace_files_factory.found_specifiers() if !test_flags.permit_no_files && !factory.found_specifiers() {
{
return Err(anyhow!("No test modules found")); return Err(anyhow!("No test modules found"));
} }
workspace_files_factory.check().await?; factory.check().await?;
if test_flags.no_run { if test_flags.no_run {
return Ok(()); return Ok(());
} }
let initial_cwd = workspace_files_factory.initial_cwd(); let initial_cwd = factory.initial_cwd();
test_specifiers( test_specifiers(
&workspace_files_factory, &factory,
None, None,
TestSpecifiersOptions { TestSpecifiersOptions {
cwd: Url::from_directory_path(initial_cwd).map_err(|_| { cwd: Url::from_directory_path(initial_cwd).map_err(|_| {
anyhow!( anyhow!(
"Unable to construct URL from the path of cwd: {}", "Unable to construct URL from the path of cwd: {}",
cli_options.initial_cwd().to_string_lossy(), factory.cli_options.initial_cwd().to_string_lossy(),
) )
})?, })?,
concurrent_jobs: test_flags concurrent_jobs: test_flags
@ -1612,8 +1598,8 @@ pub async fn run_tests_with_watch(
.into_iter() .into_iter()
.map(|(d, o)| (d, o.files)) .map(|(d, o)| (d, o.files))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let workspace_files_factory = let factory =
WorkspaceFilesFactory::from_workspace_dirs_with_files( CliFactoryWithWorkspaceFiles::from_workspace_dirs_with_files(
workspace_dirs_with_files, workspace_dirs_with_files,
|patterns, cli_options, file_fetcher, doc| { |patterns, cli_options, file_fetcher, doc| {
collect_specifiers_for_tests( collect_specifiers_for_tests(
@ -1626,26 +1612,26 @@ pub async fn run_tests_with_watch(
}, },
test_flags.doc, test_flags.doc,
Some(extract_doc_tests), Some(extract_doc_tests),
&cli_options, cli_options,
Some(&watcher_communicator), Some(&watcher_communicator),
) )
.await?; .await?;
workspace_files_factory.check().await?; factory.check().await?;
if test_flags.no_run { if test_flags.no_run {
return Ok(()); return Ok(());
} }
let initial_cwd = workspace_files_factory.initial_cwd(); let initial_cwd = factory.initial_cwd();
test_specifiers( test_specifiers(
&workspace_files_factory, &factory,
changed_paths.map(|p| p.into_iter().collect()).as_ref(), changed_paths.map(|p| p.into_iter().collect()).as_ref(),
TestSpecifiersOptions { TestSpecifiersOptions {
cwd: Url::from_directory_path(initial_cwd).map_err(|_| { cwd: Url::from_directory_path(initial_cwd).map_err(|_| {
anyhow!( anyhow!(
"Unable to construct URL from the path of cwd: {}", "Unable to construct URL from the path of cwd: {}",
cli_options.initial_cwd().to_string_lossy(), factory.cli_options.initial_cwd().to_string_lossy(),
) )
})?, })?,
concurrent_jobs: test_flags concurrent_jobs: test_flags

View file

@ -1,2 +1 @@
Check file:///[WILDLINE]/non_existent.ts
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/non_existent.ts'. error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/non_existent.ts'.

View file

@ -1,3 +1,2 @@
Download http://localhost:4545/missing_non_existent.ts Download http://localhost:4545/missing_non_existent.ts
Check http://localhost:4545/missing_non_existent.ts
error: TS2307 [ERROR]: Cannot find module 'http://localhost:4545/missing_non_existent.ts'. error: TS2307 [ERROR]: Cannot find module 'http://localhost:4545/missing_non_existent.ts'.