// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::future::Future; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; use deno_cache_dir::npm::NpmCacheDir; use deno_config::workspace::Workspace; use deno_config::workspace::WorkspaceDirectory; use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::futures::FutureExt; use deno_core::serde_json; use deno_core::url::Url; use deno_core::FeatureChecker; use deno_error::JsErrorBox; use deno_lib::args::get_root_cert_store; use deno_lib::args::resolve_npm_resolution_snapshot; use deno_lib::args::CaData; use deno_lib::args::NpmProcessStateKind; use deno_lib::args::NPM_PROCESS_STATE; use deno_lib::loader::NpmModuleLoader; use deno_lib::npm::create_npm_process_state_provider; use deno_lib::npm::NpmRegistryReadPermissionChecker; use deno_lib::npm::NpmRegistryReadPermissionCheckerMode; use deno_lib::worker::LibMainWorkerFactory; use deno_lib::worker::LibMainWorkerOptions; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm_cache::NpmCacheSetting; use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::factory::ConfigDiscoveryOption; use deno_resolver::factory::DenoDirPathProviderOptions; use deno_resolver::factory::NpmProcessStateOptions; use deno_resolver::factory::ResolverFactoryOptions; use deno_resolver::factory::SpecifiedImportMapProvider; use deno_resolver::npm::managed::NpmResolutionCell; use deno_resolver::npm::DenoInNpmPackageChecker; use deno_resolver::workspace::WorkspaceResolver; use deno_runtime::deno_fs; use deno_runtime::deno_fs::RealFs; use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::RootCertStoreProvider; use deno_runtime::deno_web::BlobStore; use deno_runtime::inspector_server::InspectorServer; use deno_runtime::permissions::RuntimePermissionDescriptorParser; use node_resolver::analyze::NodeCodeTranslator; use node_resolver::cache::NodeResolutionThreadLocalCache; use once_cell::sync::OnceCell; use sys_traits::EnvCurrentDir; use crate::args::deno_json::TsConfigResolver; use crate::args::CliOptions; use crate::args::ConfigFlag; use crate::args::DenoSubcommand; use crate::args::Flags; use crate::args::NpmInstallDepsProvider; use crate::args::WorkspaceExternalImportMapLoader; use crate::cache::Caches; use crate::cache::CodeCache; use crate::cache::DenoDir; use crate::cache::DenoDirProvider; use crate::cache::EmitCache; use crate::cache::GlobalHttpCache; use crate::cache::HttpCache; use crate::cache::ModuleInfoCache; use crate::cache::NodeAnalysisCache; use crate::cache::ParsedSourceCache; use crate::emit::Emitter; use crate::file_fetcher::CliFileFetcher; use crate::file_fetcher::TextDecodedFile; use crate::graph_container::MainModuleGraphContainer; use crate::graph_util::FileWatcherReporter; use crate::graph_util::ModuleGraphBuilder; use crate::graph_util::ModuleGraphCreator; use crate::http_util::HttpClientProvider; use crate::module_loader::CliModuleLoaderFactory; use crate::module_loader::ModuleLoadPreparer; use crate::node::CliCjsCodeAnalyzer; use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; use crate::npm::installer::NpmInstaller; use crate::npm::installer::NpmResolutionInstaller; use crate::npm::CliNpmCache; use crate::npm::CliNpmCacheHttpClient; use crate::npm::CliNpmRegistryInfoProvider; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CliNpmTarballCache; use crate::npm::NpmResolutionInitializer; use crate::resolver::CliCjsTracker; use crate::resolver::CliDenoResolver; use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; use crate::resolver::FoundPackageJsonDepFlag; use crate::standalone::binary::DenoCompileBinaryWriter; use crate::sys::CliSys; use crate::tools::check::TypeChecker; use crate::tools::coverage::CoverageCollector; use crate::tools::lint::LintRuleProvider; use crate::tools::run::hmr::HmrRunner; use crate::tsc::TypeCheckingCjsTracker; use crate::util::file_watcher::WatcherCommunicator; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::worker::CliMainWorkerFactory; use crate::worker::CliMainWorkerOptions; struct CliRootCertStoreProvider { cell: OnceCell, maybe_root_path: Option, maybe_ca_stores: Option>, maybe_ca_data: Option, } impl CliRootCertStoreProvider { pub fn new( maybe_root_path: Option, maybe_ca_stores: Option>, maybe_ca_data: Option, ) -> Self { Self { cell: Default::default(), maybe_root_path, maybe_ca_stores, maybe_ca_data, } } } impl RootCertStoreProvider for CliRootCertStoreProvider { fn get_or_try_init(&self) -> Result<&RootCertStore, JsErrorBox> { self .cell .get_or_try_init(|| { get_root_cert_store( self.maybe_root_path.clone(), self.maybe_ca_stores.clone(), self.maybe_ca_data.clone(), ) }) .map_err(JsErrorBox::from_err) } } #[derive(Debug)] struct CliSpecifiedImportMapProvider { cli_options: Arc, file_fetcher: Arc, workspace_external_import_map_loader: Arc, } #[async_trait::async_trait(?Send)] impl SpecifiedImportMapProvider for CliSpecifiedImportMapProvider { async fn get( &self, ) -> Result, AnyError> { async fn resolve_import_map_value_from_specifier( specifier: &Url, file_fetcher: &CliFileFetcher, ) -> Result { if specifier.scheme() == "data" { let data_url_text = deno_media_type::data_url::RawDataUrl::parse(specifier)?.decode()?; Ok(serde_json::from_str(&data_url_text)?) } else { let file = TextDecodedFile::decode( file_fetcher.fetch_bypass_permissions(specifier).await?, )?; Ok(serde_json::from_str(&file.source)?) } } let maybe_import_map_specifier = self.cli_options.resolve_specified_import_map_specifier()?; match maybe_import_map_specifier { Some(specifier) => { let value = resolve_import_map_value_from_specifier( &specifier, &self.file_fetcher, ) .await .with_context(|| { format!("Unable to load '{}' import map", specifier) })?; Ok(Some(deno_resolver::workspace::SpecifiedImportMap { base_url: specifier, value, })) } None => { if let Some(import_map) = self.workspace_external_import_map_loader.get_or_load()? { let path_url = deno_path_util::url_from_file_path(&import_map.path)?; Ok(Some(deno_resolver::workspace::SpecifiedImportMap { base_url: path_url, value: import_map.value.clone(), })) } else { Ok(None) } } } } } pub type CliWorkspaceFactory = deno_resolver::factory::WorkspaceFactory; pub type CliDenoDirPathProvider = deno_resolver::factory::DenoDirPathProvider; pub type CliResolverFactory = deno_resolver::factory::ResolverFactory; pub struct Deferred(once_cell::unsync::OnceCell); impl Default for Deferred { fn default() -> Self { Self(once_cell::unsync::OnceCell::default()) } } impl Deferred { #[inline(always)] pub fn get_or_try_init( &self, create: impl FnOnce() -> Result, ) -> Result<&T, AnyError> { self.0.get_or_try_init(create) } #[inline(always)] pub fn get_or_init(&self, create: impl FnOnce() -> T) -> &T { self.0.get_or_init(create) } pub async fn get_or_try_init_async( &self, // some futures passed here are boxed because it was discovered // that they were called a lot, causing other futures to get // really big causing stack overflows on Windows create: impl Future>, ) -> Result<&T, AnyError> { if self.0.get().is_none() { // todo(dsherret): it would be more ideal if this enforced a // single executor and then we could make some initialization // concurrent let val = create.await?; _ = self.0.set(val); } Ok(self.0.get().unwrap()) } } #[derive(Default)] struct CliFactoryServices { blob_store: Deferred>, caches: Deferred>, cjs_tracker: Deferred>, cli_options: Deferred>, code_cache: Deferred>, deno_dir_path_provider: Deferred>, deno_dir_provider: Deferred>, emit_cache: Deferred>, emitter: Deferred>, feature_checker: Deferred>, file_fetcher: Deferred>, found_pkg_json_dep_flag: Arc, fs: Deferred>, http_client_provider: Deferred>, main_graph_container: Deferred>, maybe_file_watcher_reporter: Deferred>, maybe_inspector_server: Deferred>>, module_graph_builder: Deferred>, module_graph_creator: Deferred>, module_info_cache: Deferred>, module_load_preparer: Deferred>, node_code_translator: Deferred>, npm_cache: Deferred>, npm_cache_http_client: Deferred>, npm_graph_resolver: Deferred>, npm_installer: Deferred>, npm_registry_info_provider: Deferred>, npm_resolution_initializer: Deferred>, npm_resolution_installer: Deferred>, npm_tarball_cache: Deferred>, parsed_source_cache: Deferred>, permission_desc_parser: Deferred>>, resolver: Deferred>, resolver_factory: Deferred>, root_cert_store_provider: Deferred>, root_permissions_container: Deferred, text_only_progress_bar: Deferred, tsconfig_resolver: Deferred>, type_checker: Deferred>, workspace_factory: Deferred>, workspace_external_import_map_loader: Deferred>, } #[derive(Debug, Default)] struct CliFactoryOverrides { initial_cwd: Option, workspace_directory: Option>, } pub struct CliFactory { watcher_communicator: Option>, flags: Arc, services: CliFactoryServices, overrides: CliFactoryOverrides, } impl CliFactory { pub fn from_flags(flags: Arc) -> Self { Self { flags, watcher_communicator: None, services: Default::default(), overrides: Default::default(), } } pub fn from_flags_for_watcher( flags: Arc, watcher_communicator: Arc, ) -> Self { CliFactory { watcher_communicator: Some(watcher_communicator), flags, services: Default::default(), overrides: Default::default(), } } pub fn set_initial_cwd(&mut self, initial_cwd: PathBuf) { self.overrides.initial_cwd = Some(initial_cwd); } pub fn set_workspace_dir(&mut self, dir: Arc) { self.overrides.workspace_directory = Some(dir); } pub fn cli_options(&self) -> Result<&Arc, AnyError> { self.services.cli_options.get_or_try_init(|| { let workspace_factory = self.workspace_factory()?; let workspace_directory = workspace_factory.workspace_directory()?; let maybe_external_import_map = self.workspace_external_import_map_loader()?.get_or_load()?; CliOptions::from_flags( &self.sys(), self.flags.clone(), workspace_factory.initial_cwd().clone(), maybe_external_import_map, workspace_directory.clone(), ) .map(Arc::new) }) } pub fn deno_dir_path_provider(&self) -> &Arc { self.services.deno_dir_path_provider.get_or_init(|| { Arc::new(CliDenoDirPathProvider::new( self.sys(), DenoDirPathProviderOptions { maybe_custom_root: self.flags.internal.cache_path.clone(), }, )) }) } pub fn deno_dir_provider(&self) -> &Arc { self.services.deno_dir_provider.get_or_init(|| { Arc::new(DenoDirProvider::new( self.sys(), self.deno_dir_path_provider().clone(), )) }) } pub fn deno_dir(&self) -> Result<&DenoDir, AnyError> { Ok(self.deno_dir_provider().get_or_create()?) } pub fn caches(&self) -> Result<&Arc, AnyError> { self.services.caches.get_or_try_init(|| { let cli_options = self.cli_options()?; let caches = Arc::new(Caches::new(self.deno_dir_provider().clone())); // Warm up the caches we know we'll likely need based on the CLI mode match cli_options.sub_command() { DenoSubcommand::Run(_) | DenoSubcommand::Serve(_) | DenoSubcommand::Bench(_) | DenoSubcommand::Test(_) | DenoSubcommand::Check(_) => { _ = caches.dep_analysis_db(); _ = caches.node_analysis_db(); if cli_options.type_check_mode().is_true() { _ = caches.fast_check_db(); _ = caches.type_checking_cache_db(); } if cli_options.code_cache_enabled() { _ = caches.code_cache_db(); } } _ => {} } Ok(caches) }) } pub fn blob_store(&self) -> &Arc { self.services.blob_store.get_or_init(Default::default) } pub fn root_cert_store_provider(&self) -> &Arc { self.services.root_cert_store_provider.get_or_init(|| { Arc::new(CliRootCertStoreProvider::new( None, self.flags.ca_stores.clone(), self.flags.ca_data.clone(), )) }) } pub fn text_only_progress_bar(&self) -> &ProgressBar { self .services .text_only_progress_bar .get_or_init(|| ProgressBar::new(ProgressBarStyle::TextOnly)) } pub fn global_http_cache(&self) -> Result<&Arc, AnyError> { Ok(self.workspace_factory()?.global_http_cache()?) } pub fn http_cache(&self) -> Result<&Arc, AnyError> { Ok(self.workspace_factory()?.http_cache()?) } pub fn http_client_provider(&self) -> &Arc { self.services.http_client_provider.get_or_init(|| { Arc::new(HttpClientProvider::new( Some(self.root_cert_store_provider().clone()), self.flags.unsafely_ignore_certificate_errors.clone(), )) }) } pub fn file_fetcher(&self) -> Result<&Arc, AnyError> { self.services.file_fetcher.get_or_try_init(|| { let cli_options = self.cli_options()?; Ok(Arc::new(CliFileFetcher::new( self.http_cache()?.clone(), self.http_client_provider().clone(), self.sys(), self.blob_store().clone(), Some(self.text_only_progress_bar().clone()), !cli_options.no_remote(), cli_options.cache_setting(), log::Level::Info, ))) }) } pub fn fs(&self) -> &Arc { self.services.fs.get_or_init(|| Arc::new(RealFs)) } pub fn sys(&self) -> CliSys { CliSys::default() // very cheap to make } pub fn in_npm_pkg_checker( &self, ) -> Result<&DenoInNpmPackageChecker, AnyError> { self.resolver_factory()?.in_npm_package_checker() } pub fn npm_cache(&self) -> Result<&Arc, AnyError> { self.services.npm_cache.get_or_try_init(|| { let cli_options = self.cli_options()?; Ok(Arc::new(CliNpmCache::new( self.npm_cache_dir()?.clone(), self.sys(), NpmCacheSetting::from_cache_setting(&cli_options.cache_setting()), self.npmrc()?.clone(), ))) }) } pub fn npm_cache_dir(&self) -> Result<&Arc, AnyError> { Ok(self.workspace_factory()?.npm_cache_dir()?) } pub fn npm_cache_http_client(&self) -> &Arc { self.services.npm_cache_http_client.get_or_init(|| { Arc::new(CliNpmCacheHttpClient::new( self.http_client_provider().clone(), self.text_only_progress_bar().clone(), )) }) } pub fn npm_graph_resolver( &self, ) -> Result<&Arc, AnyError> { self.services.npm_graph_resolver.get_or_try_init(|| { let cli_options = self.cli_options()?; Ok(Arc::new(CliNpmGraphResolver::new( self.npm_installer_if_managed()?.cloned(), self.services.found_pkg_json_dep_flag.clone(), cli_options.unstable_bare_node_builtins(), cli_options.default_npm_caching_strategy(), ))) }) } pub fn npm_installer_if_managed( &self, ) -> Result>, AnyError> { if self.resolver_factory()?.use_byonm()? || self.cli_options()?.no_npm() { Ok(None) } else { Ok(Some(self.npm_installer()?)) } } pub fn npm_installer(&self) -> Result<&Arc, AnyError> { self.services.npm_installer.get_or_try_init(|| { let cli_options = self.cli_options()?; let workspace_factory = self.workspace_factory()?; Ok(Arc::new(NpmInstaller::new( self.npm_cache()?.clone(), Arc::new(NpmInstallDepsProvider::from_workspace( cli_options.workspace(), )), self.npm_resolution()?.clone(), self.npm_resolution_initializer()?.clone(), self.npm_resolution_installer()?.clone(), self.text_only_progress_bar(), self.sys(), self.npm_tarball_cache()?.clone(), cli_options.maybe_lockfile().cloned(), workspace_factory .node_modules_dir_path()? .map(|p| p.to_path_buf()), cli_options.lifecycle_scripts_config(), cli_options.npm_system_info(), ))) }) } pub fn npm_registry_info_provider( &self, ) -> Result<&Arc, AnyError> { self .services .npm_registry_info_provider .get_or_try_init(|| { Ok(Arc::new(CliNpmRegistryInfoProvider::new( self.npm_cache()?.clone(), self.npm_cache_http_client().clone(), self.npmrc()?.clone(), ))) }) } pub fn npm_resolution(&self) -> Result<&Arc, AnyError> { Ok(self.resolver_factory()?.npm_resolution()) } pub fn npm_resolution_initializer( &self, ) -> Result<&Arc, AnyError> { self .services .npm_resolution_initializer .get_or_try_init(|| { let cli_options = self.cli_options()?; Ok(Arc::new(NpmResolutionInitializer::new( self.npm_registry_info_provider()?.clone(), self.npm_resolution()?.clone(), match resolve_npm_resolution_snapshot()? { Some(snapshot) => { CliNpmResolverManagedSnapshotOption::Specified(Some(snapshot)) } None => match cli_options.maybe_lockfile() { Some(lockfile) => { CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( lockfile.clone(), ) } None => CliNpmResolverManagedSnapshotOption::Specified(None), }, }, ))) }) } pub fn npm_resolution_installer( &self, ) -> Result<&Arc, AnyError> { self.services.npm_resolution_installer.get_or_try_init(|| { let cli_options = self.cli_options()?; Ok(Arc::new(NpmResolutionInstaller::new( self.npm_registry_info_provider()?.clone(), self.npm_resolution()?.clone(), cli_options.maybe_lockfile().cloned(), ))) }) } pub async fn npm_resolver(&self) -> Result<&CliNpmResolver, AnyError> { self.initialize_npm_resolution_if_managed().await?; self.resolver_factory()?.npm_resolver() } pub fn npm_tarball_cache( &self, ) -> Result<&Arc, AnyError> { self.services.npm_tarball_cache.get_or_try_init(|| { Ok(Arc::new(CliNpmTarballCache::new( self.npm_cache()?.clone(), self.npm_cache_http_client().clone(), self.sys(), self.npmrc()?.clone(), ))) }) } pub fn npmrc(&self) -> Result<&Arc, AnyError> { Ok(self.workspace_factory()?.npmrc()?) } pub fn resolver_factory(&self) -> Result<&Arc, AnyError> { self.services.resolver_factory.get_or_try_init(|| { Ok(Arc::new(CliResolverFactory::new( self.workspace_factory()?.clone(), ResolverFactoryOptions { conditions_from_resolution_mode: Default::default(), node_resolution_cache: Some(Arc::new(NodeResolutionThreadLocalCache)), npm_system_info: self.flags.subcommand.npm_system_info(), specified_import_map: Some(Box::new(CliSpecifiedImportMapProvider { cli_options: self.cli_options()?.clone(), file_fetcher: self.file_fetcher()?.clone(), workspace_external_import_map_loader: self .workspace_external_import_map_loader()? .clone(), })), unstable_sloppy_imports: self.flags.unstable_config.sloppy_imports, package_json_cache: Some(Arc::new( node_resolver::PackageJsonThreadLocalCache, )), package_json_dep_resolution: match &self.flags.subcommand { DenoSubcommand::Publish(_) => { // the node_modules directory is not published to jsr, so resolve // dependencies via the package.json rather than using node resolution Some(deno_resolver::workspace::PackageJsonDepResolution::Enabled) } _ => None, }, }, ))) }) } pub fn workspace(&self) -> Result<&Arc, AnyError> { Ok(&self.workspace_directory()?.workspace) } pub fn workspace_directory( &self, ) -> Result<&Arc, AnyError> { Ok(self.workspace_factory()?.workspace_directory()?) } fn workspace_factory(&self) -> Result<&Arc, AnyError> { self.services.workspace_factory.get_or_try_init(|| { let initial_cwd = match self.overrides.initial_cwd.clone() { Some(v) => v, None => self .sys() .env_current_dir() .with_context(|| "Failed getting cwd.")?, }; let options = new_workspace_factory_options( &initial_cwd, &self.flags, self.deno_dir_path_provider().clone(), ); let mut factory = CliWorkspaceFactory::new(self.sys(), initial_cwd, options); if let Some(workspace_dir) = &self.overrides.workspace_directory { factory.set_workspace_directory(workspace_dir.clone()); } Ok(Arc::new(factory)) }) } pub async fn workspace_resolver( &self, ) -> Result<&Arc>, AnyError> { self.initialize_npm_resolution_if_managed().await?; self.resolver_factory()?.workspace_resolver().await } pub async fn deno_resolver(&self) -> Result<&Arc, AnyError> { self.initialize_npm_resolution_if_managed().await?; self.resolver_factory()?.deno_resolver().await } pub async fn resolver(&self) -> Result<&Arc, AnyError> { self .services .resolver .get_or_try_init_async( async { Ok(Arc::new(CliResolver::new( self.deno_resolver().await?.clone(), self.services.found_pkg_json_dep_flag.clone(), ))) } .boxed_local(), ) .await } pub fn maybe_file_watcher_reporter(&self) -> &Option { let maybe_file_watcher_reporter = self .watcher_communicator .as_ref() .map(|i| FileWatcherReporter::new(i.clone())); self .services .maybe_file_watcher_reporter .get_or_init(|| maybe_file_watcher_reporter) } pub fn emit_cache(&self) -> Result<&Arc, AnyError> { self.services.emit_cache.get_or_try_init(|| { Ok(Arc::new(EmitCache::new(self.deno_dir()?.gen_cache.clone()))) }) } pub fn module_info_cache(&self) -> Result<&Arc, AnyError> { self.services.module_info_cache.get_or_try_init(|| { Ok(Arc::new(ModuleInfoCache::new( self.caches()?.dep_analysis_db(), self.parsed_source_cache().clone(), ))) }) } pub fn code_cache(&self) -> Result<&Arc, AnyError> { self.services.code_cache.get_or_try_init(|| { Ok(Arc::new(CodeCache::new(self.caches()?.code_cache_db()))) }) } pub fn parsed_source_cache(&self) -> &Arc { self .services .parsed_source_cache .get_or_init(Default::default) } pub fn emitter(&self) -> Result<&Arc, AnyError> { self.services.emitter.get_or_try_init(|| { Ok(Arc::new(Emitter::new( self.cjs_tracker()?.clone(), self.emit_cache()?.clone(), self.parsed_source_cache().clone(), self.tsconfig_resolver()?.clone(), ))) }) } pub async fn lint_rule_provider(&self) -> Result { Ok(LintRuleProvider::new(Some( self.workspace_resolver().await?.clone(), ))) } pub async fn node_resolver(&self) -> Result<&Arc, AnyError> { self.initialize_npm_resolution_if_managed().await?; self.resolver_factory()?.node_resolver() } async fn initialize_npm_resolution_if_managed(&self) -> Result<(), AnyError> { let npm_resolver = self.resolver_factory()?.npm_resolver()?; if npm_resolver.is_managed() { self .npm_resolution_initializer()? .ensure_initialized() .await?; } Ok(()) } pub async fn node_code_translator( &self, ) -> Result<&Arc, AnyError> { self .services .node_code_translator .get_or_try_init_async(async { let node_resolver = self.node_resolver().await?.clone(); let cjs_code_analyzer = self.create_cjs_code_analyzer()?; Ok(Arc::new(NodeCodeTranslator::new( cjs_code_analyzer, self.in_npm_pkg_checker()?.clone(), node_resolver, self.npm_resolver().await?.clone(), self.pkg_json_resolver()?.clone(), self.sys(), ))) }) .await } fn create_cjs_code_analyzer(&self) -> Result { let caches = self.caches()?; let node_analysis_cache = NodeAnalysisCache::new(caches.node_analysis_db()); Ok(CliCjsCodeAnalyzer::new( node_analysis_cache, self.cjs_tracker()?.clone(), self.fs().clone(), Some(self.parsed_source_cache().clone()), )) } pub fn npm_req_resolver(&self) -> Result<&Arc, AnyError> { self.resolver_factory()?.npm_req_resolver() } pub fn pkg_json_resolver( &self, ) -> Result<&Arc, AnyError> { Ok(self.resolver_factory()?.pkg_json_resolver()) } pub fn tsconfig_resolver(&self) -> Result<&Arc, AnyError> { self.services.tsconfig_resolver.get_or_try_init(|| { let workspace = self.workspace()?; Ok(Arc::new(TsConfigResolver::from_workspace(workspace))) }) } pub async fn type_checker(&self) -> Result<&Arc, AnyError> { self .services .type_checker .get_or_try_init_async(async { let cli_options = self.cli_options()?; Ok(Arc::new(TypeChecker::new( self.caches()?.clone(), Arc::new(TypeCheckingCjsTracker::new( self.cjs_tracker()?.clone(), self.module_info_cache()?.clone(), )), cli_options.clone(), self.module_graph_builder().await?.clone(), self.node_resolver().await?.clone(), self.npm_installer_if_managed()?.cloned(), self.npm_resolver().await?.clone(), self.sys(), self.tsconfig_resolver()?.clone(), ))) }) .await } pub async fn module_graph_builder( &self, ) -> Result<&Arc, AnyError> { self .services .module_graph_builder .get_or_try_init_async(async { let cli_options = self.cli_options()?; Ok(Arc::new(ModuleGraphBuilder::new( self.caches()?.clone(), self.cjs_tracker()?.clone(), cli_options.clone(), self.file_fetcher()?.clone(), self.global_http_cache()?.clone(), self.in_npm_pkg_checker()?.clone(), cli_options.maybe_lockfile().cloned(), self.maybe_file_watcher_reporter().clone(), self.module_info_cache()?.clone(), self.npm_graph_resolver()?.clone(), self.npm_installer_if_managed()?.cloned(), self.npm_resolver().await?.clone(), self.parsed_source_cache().clone(), self.resolver().await?.clone(), self.root_permissions_container()?.clone(), self.sys(), self.tsconfig_resolver()?.clone(), ))) }) .await } pub async fn module_graph_creator( &self, ) -> Result<&Arc, AnyError> { self .services .module_graph_creator .get_or_try_init_async(async { let cli_options = self.cli_options()?; Ok(Arc::new(ModuleGraphCreator::new( cli_options.clone(), self.npm_installer_if_managed()?.cloned(), self.module_graph_builder().await?.clone(), self.type_checker().await?.clone(), ))) }) .await } pub async fn main_module_graph_container( &self, ) -> Result<&Arc, AnyError> { self .services .main_graph_container .get_or_try_init_async(async { Ok(Arc::new(MainModuleGraphContainer::new( self.cli_options()?.clone(), self.module_load_preparer().await?.clone(), self.root_permissions_container()?.clone(), ))) }) .await } pub fn maybe_inspector_server( &self, ) -> Result<&Option>, AnyError> { self.services.maybe_inspector_server.get_or_try_init(|| { let cli_options = self.cli_options()?; match cli_options.resolve_inspector_server() { Ok(server) => Ok(server.map(Arc::new)), Err(err) => Err(err), } }) } pub async fn module_load_preparer( &self, ) -> Result<&Arc, AnyError> { self .services .module_load_preparer .get_or_try_init_async(async { let cli_options = self.cli_options()?; Ok(Arc::new(ModuleLoadPreparer::new( cli_options.clone(), cli_options.maybe_lockfile().cloned(), self.module_graph_builder().await?.clone(), self.text_only_progress_bar().clone(), self.type_checker().await?.clone(), ))) }) .await } pub fn cjs_tracker(&self) -> Result<&Arc, AnyError> { self.services.cjs_tracker.get_or_try_init(|| { let options = self.cli_options()?; Ok(Arc::new(CliCjsTracker::new( self.in_npm_pkg_checker()?.clone(), self.pkg_json_resolver()?.clone(), if options.is_node_main() || options.unstable_detect_cjs() { IsCjsResolutionMode::ImplicitTypeCommonJs } else if options.detect_cjs() { IsCjsResolutionMode::ExplicitTypeCommonJs } else { IsCjsResolutionMode::Disabled }, ))) }) } pub fn permission_desc_parser( &self, ) -> Result<&Arc>, AnyError> { self.services.permission_desc_parser.get_or_try_init(|| { Ok(Arc::new(RuntimePermissionDescriptorParser::new(self.sys()))) }) } pub fn feature_checker(&self) -> Result<&Arc, AnyError> { self.services.feature_checker.get_or_try_init(|| { let cli_options = self.cli_options()?; let mut checker = FeatureChecker::default(); checker.set_exit_cb(Box::new(crate::unstable_exit_cb)); let unstable_features = cli_options.unstable_features(); for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS { if unstable_features.contains(&granular_flag.name.to_string()) { checker.enable_feature(granular_flag.name); } } Ok(Arc::new(checker)) }) } pub async fn create_compile_binary_writer( &self, ) -> Result { let cli_options = self.cli_options()?; Ok(DenoCompileBinaryWriter::new( self.create_cjs_code_analyzer()?, self.cjs_tracker()?, self.cli_options()?, self.deno_dir()?, self.emitter()?, self.http_client_provider(), self.npm_resolver().await?, self.workspace_resolver().await?.as_ref(), cli_options.npm_system_info(), )) } pub fn root_permissions_container( &self, ) -> Result<&PermissionsContainer, AnyError> { self .services .root_permissions_container .get_or_try_init(|| { let desc_parser = self.permission_desc_parser()?.clone(); let permissions = Permissions::from_options( desc_parser.as_ref(), &self.cli_options()?.permissions_options(), )?; Ok(PermissionsContainer::new(desc_parser, permissions)) }) } fn workspace_external_import_map_loader( &self, ) -> Result<&Arc, AnyError> { self .services .workspace_external_import_map_loader .get_or_try_init(|| { Ok(Arc::new(WorkspaceExternalImportMapLoader::new( self.sys(), self.workspace_directory()?.workspace.clone(), ))) }) } pub async fn create_cli_main_worker_factory( &self, ) -> Result { let cli_options = self.cli_options()?; let fs = self.fs(); let node_resolver = self.node_resolver().await?; let npm_resolver = self.npm_resolver().await?; let cli_npm_resolver = self.npm_resolver().await?.clone(); let in_npm_pkg_checker = self.in_npm_pkg_checker()?; let maybe_file_watcher_communicator = if cli_options.has_hmr() { Some(self.watcher_communicator.clone().unwrap()) } else { None }; let node_code_translator = self.node_code_translator().await?; let cjs_tracker = self.cjs_tracker()?.clone(); let pkg_json_resolver = self.pkg_json_resolver()?.clone(); let npm_req_resolver = self.npm_req_resolver()?; let workspace_factory = self.workspace_factory()?; let npm_registry_permission_checker = { let mode = if self.resolver_factory()?.use_byonm()? { NpmRegistryReadPermissionCheckerMode::Byonm } else if let Some(node_modules_dir) = workspace_factory.node_modules_dir_path()? { NpmRegistryReadPermissionCheckerMode::Local( node_modules_dir.to_path_buf(), ) } else { NpmRegistryReadPermissionCheckerMode::Global( self.npm_cache_dir()?.root_dir().to_path_buf(), ) }; Arc::new(NpmRegistryReadPermissionChecker::new(self.sys(), mode)) }; let module_loader_factory = CliModuleLoaderFactory::new( cli_options, cjs_tracker, if cli_options.code_cache_enabled() { Some(self.code_cache()?.clone()) } else { None }, self.emitter()?.clone(), in_npm_pkg_checker.clone(), self.main_module_graph_container().await?.clone(), self.module_load_preparer().await?.clone(), node_code_translator.clone(), node_resolver.clone(), NpmModuleLoader::new( self.cjs_tracker()?.clone(), node_code_translator.clone(), self.sys(), ), npm_registry_permission_checker, npm_req_resolver.clone(), cli_npm_resolver.clone(), self.parsed_source_cache().clone(), self.resolver().await?.clone(), self.sys(), ); let lib_main_worker_factory = LibMainWorkerFactory::new( self.blob_store().clone(), if cli_options.code_cache_enabled() { Some(self.code_cache()?.clone()) } else { None }, self.feature_checker()?.clone(), fs.clone(), self.maybe_inspector_server()?.clone(), Box::new(module_loader_factory), node_resolver.clone(), create_npm_process_state_provider(npm_resolver), pkg_json_resolver, self.root_cert_store_provider().clone(), cli_options.resolve_storage_key_resolver(), self.sys(), self.create_lib_main_worker_options()?, ); Ok(CliMainWorkerFactory::new( lib_main_worker_factory, maybe_file_watcher_communicator, cli_options.maybe_lockfile().cloned(), self.npm_installer_if_managed()?.cloned(), npm_resolver.clone(), self.sys(), self.create_cli_main_worker_options()?, self.root_permissions_container()?.clone(), )) } fn create_lib_main_worker_options( &self, ) -> Result { let cli_options = self.cli_options()?; let workspace_factory = self.workspace_factory()?; Ok(LibMainWorkerOptions { argv: cli_options.argv().clone(), // This optimization is only available for "run" subcommand // because we need to register new ops for testing and jupyter // integration. skip_op_registration: cli_options.sub_command().is_run(), log_level: cli_options.log_level().unwrap_or(log::Level::Info).into(), enable_op_summary_metrics: cli_options.enable_op_summary_metrics(), enable_testing_features: cli_options.enable_testing_features(), has_node_modules_dir: workspace_factory .node_modules_dir_path()? .is_some(), inspect_brk: cli_options.inspect_brk().is_some(), inspect_wait: cli_options.inspect_wait().is_some(), strace_ops: cli_options.strace_ops().clone(), is_inspecting: cli_options.is_inspecting(), location: cli_options.location_flag().clone(), // if the user ran a binary command, we'll need to set process.argv[0] // to be the name of the binary command instead of deno argv0: cli_options .take_binary_npm_command_name() .or(std::env::args().next()), node_debug: std::env::var("NODE_DEBUG").ok(), origin_data_folder_path: Some(self.deno_dir()?.origin_data_folder_path()), seed: cli_options.seed(), unsafely_ignore_certificate_errors: cli_options .unsafely_ignore_certificate_errors() .clone(), node_ipc: cli_options.node_ipc_fd(), serve_port: cli_options.serve_port(), serve_host: cli_options.serve_host(), otel_config: self.cli_options()?.otel_config(), startup_snapshot: deno_snapshots::CLI_SNAPSHOT, }) } fn create_cli_main_worker_options( &self, ) -> Result { let cli_options = self.cli_options()?; let create_hmr_runner = if cli_options.has_hmr() { let watcher_communicator = self.watcher_communicator.clone().unwrap(); let emitter = self.emitter()?.clone(); let fn_: crate::worker::CreateHmrRunnerCb = Box::new(move |session| { Box::new(HmrRunner::new( emitter.clone(), session, watcher_communicator.clone(), )) }); Some(fn_) } else { None }; let create_coverage_collector = if let Some(coverage_dir) = cli_options.coverage_dir() { let coverage_dir = PathBuf::from(coverage_dir); let fn_: crate::worker::CreateCoverageCollectorCb = Box::new(move |session| { Box::new(CoverageCollector::new(coverage_dir.clone(), session)) }); Some(fn_) } else { None }; Ok(CliMainWorkerOptions { needs_test_modules: cli_options.sub_command().needs_test(), create_hmr_runner, create_coverage_collector, default_npm_caching_strategy: cli_options.default_npm_caching_strategy(), }) } } fn new_workspace_factory_options( initial_cwd: &Path, flags: &Flags, deno_dir_path_provider: Arc, ) -> deno_resolver::factory::WorkspaceFactoryOptions { deno_resolver::factory::WorkspaceFactoryOptions { additional_config_file_names: if matches!( flags.subcommand, DenoSubcommand::Publish(..) ) { &["jsr.json", "jsr.jsonc"] } else { &[] }, config_discovery: match &flags.config_flag { ConfigFlag::Discover => { if let Some(start_paths) = flags.config_path_args(initial_cwd) { ConfigDiscoveryOption::Discover { start_paths } } else { ConfigDiscoveryOption::Disabled } } ConfigFlag::Path(path) => { ConfigDiscoveryOption::Path(PathBuf::from(path)) } ConfigFlag::Disabled => ConfigDiscoveryOption::Disabled, }, deno_dir_path_provider: Some(deno_dir_path_provider), // For `deno install/add/remove/init` we want to force the managed // resolver so it can set up the `node_modules/` directory. is_package_manager_subcommand: matches!( flags.subcommand, DenoSubcommand::Install(_) | DenoSubcommand::Add(_) | DenoSubcommand::Remove(_) | DenoSubcommand::Init(_) | DenoSubcommand::Outdated(_) ), no_npm: flags.no_npm, node_modules_dir: flags.node_modules_dir, npm_process_state: NPM_PROCESS_STATE.as_ref().map(|s| { NpmProcessStateOptions { node_modules_dir: s .local_node_modules_path .as_ref() .map(|s| Cow::Borrowed(s.as_str())), is_byonm: matches!(s.kind, NpmProcessStateKind::Byonm), } }), vendor: flags.vendor, } }