/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
cli/tools/installer/mod.rs
435 строк
12 KB
Bartek Iwańczuk
feat(check): type-check with native tsc by default (#35938)
17 июл 2026, 15:48
Не верифицирован
17 июл 2026, 15:48
fe70a4e
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. use std::sync::Arc; use dashmap::DashSet; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::url::Url; use deno_lib::version::DENO_VERSION; use deno_lib::version::NODE_VERSION; use deno_npm_installer::lifecycle_scripts::LifecycleScriptsWarning; use deno_package_json::EngineMismatchKind; use deno_resolver::workspace::WorkspaceResolver; pub use self::bin_name_resolver::BinNameResolver; use crate::args::Flags; use crate::args::InstallEntrypointsFlags; use crate::args::InstallFlags; use crate::args::InstallFlagsLocal; use crate::factory::CliFactory; use crate::graph_container::CollectSpecifiersOptions; use crate::graph_container::ModuleGraphContainer; use crate::npm::CliNpmResolver; use crate::sys::CliSys; use crate::util::display; mod bin_name_resolver; mod global; mod local; mod npm_compat; pub use global::uninstall; use local::CategorizedInstalledDeps; pub use local::RootTsConfigMode; use local::categorize_installed_npm_deps; pub use local::ci_command; pub use local::sync_types_command; #[derive(Default)] pub struct InstallStats { pub resolved_jsr: DashSet<String>, pub downloaded_jsr: DashSet<String>, pub reused_jsr: DashSet<String>, pub resolved_npm: DashSet<String>, pub downloaded_npm: DashSet<String>, pub intialized_npm: DashSet<String>, pub reused_npm: crate::util::sync::RelaxedAtomicCounter, } impl std::fmt::Debug for InstallStats { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("InstallStats") .field( "resolved_jsr", &self .resolved_jsr .iter() .map(|s| s.as_str().to_string()) .collect::<Vec<_>>(), ) .field( "downloaded_jsr", &self .downloaded_jsr .iter() .map(|s| s.as_str().to_string()) .collect::<Vec<_>>(), ) .field("resolved_npm", &self.resolved_npm.len()) .field("resolved_jsr_count", &self.resolved_jsr.len()) .field("downloaded_npm", &self.downloaded_npm.len()) .field("downloaded_jsr_count", &self.downloaded_jsr.len()) .field( "intialized_npm", &self .intialized_npm .iter() .map(|s| s.as_str().to_string()) .collect::<Vec<_>>(), ) .field("intialized_npm_count", &self.intialized_npm.len()) .field("reused_npm", &self.reused_npm.get()) .finish() } } #[derive(Debug)] pub struct InstallReporter { stats: Arc<InstallStats>, scripts_warnings: Arc<Mutex<Vec<LifecycleScriptsWarning>>>, deprecation_messages: Arc<Mutex<Vec<String>>>, } impl InstallReporter { pub fn new() -> Self { Self { stats: Arc::new(InstallStats::default()), scripts_warnings: Arc::new(Mutex::new(Vec::new())), deprecation_messages: Arc::new(Mutex::new(Vec::new())), } } pub fn take_scripts_warnings(&self) -> Vec<LifecycleScriptsWarning> { std::mem::take(&mut *self.scripts_warnings.lock()) } pub fn take_deprecation_message(&self) -> Vec<String> { std::mem::take(&mut *self.deprecation_messages.lock()) } } impl deno_npm_installer::InstallProgressReporter for InstallReporter { fn initializing(&self, _nv: &deno_semver::package::PackageNv) {} fn initialized(&self, nv: &deno_semver::package::PackageNv) { self.stats.intialized_npm.insert(nv.to_string()); } fn blocking(&self, _message: &str) {} fn scripts_not_run_warning( &self, warning: deno_npm_installer::lifecycle_scripts::LifecycleScriptsWarning, ) { self.scripts_warnings.lock().push(warning); } fn deprecated_message(&self, message: String) { self.deprecation_messages.lock().push(message); } } fn package_nv_from_url(url: &Url) -> Option<String> { if !matches!(url.scheme(), "http" | "https") { return None; } if !url.host_str().is_some_and(|h| h.contains("jsr.io")) { return None; } let mut parts = url.path_segments()?; let scope = parts.next()?; let name = parts.next()?; let version = parts.next()?; if version.ends_with(".json") { // don't include meta.json urls return None; } Some(format!("{scope}/{name}@{version}")) } impl deno_graph::source::Reporter for InstallReporter { fn on_resolve( &self, _req: &deno_semver::package::PackageReq, package_nv: &deno_semver::package::PackageNv, ) { self.stats.resolved_jsr.insert(package_nv.to_string()); } } impl deno_npm::resolution::Reporter for InstallReporter { fn on_resolved( &self, package_req: &deno_semver::package::PackageReq, _nv: &deno_semver::package::PackageNv, ) { self.stats.resolved_npm.insert(package_req.to_string()); } } impl deno_npm_cache::TarballCacheReporter for InstallReporter { fn download_started(&self, _nv: &deno_semver::package::PackageNv) {} fn downloaded(&self, nv: &deno_semver::package::PackageNv) { self.stats.downloaded_npm.insert(nv.to_string()); } fn reused_cache(&self, _nv: &deno_semver::package::PackageNv) { self.stats.reused_npm.inc(); } } impl deno_resolver::file_fetcher::GraphLoaderReporter for InstallReporter { fn on_load( &self, specifier: &Url, loaded_from: deno_cache_dir::file_fetcher::LoadedFrom, ) { if let Some(nv) = package_nv_from_url(specifier) { match loaded_from { deno_cache_dir::file_fetcher::LoadedFrom::Cache => { self.stats.reused_jsr.insert(nv); } deno_cache_dir::file_fetcher::LoadedFrom::Remote => { self.stats.downloaded_jsr.insert(nv); } _ => {} } } else { // it's a local file or http/https specifier } } } pub async fn install_from_entrypoints( flags: Arc<Flags>, entrypoints_flags: InstallEntrypointsFlags, ) -> Result<(), AnyError> { let started = std::time::Instant::now(); let factory = CliFactory::from_flags(flags.clone()); let emitter = factory.emitter()?; let main_graph_container = factory.main_module_graph_container().await?; let specifiers = main_graph_container.collect_specifiers( &entrypoints_flags.entrypoints, CollectSpecifiersOptions { include_ignored_specified: true, }, )?; main_graph_container .check_specifiers( &specifiers, crate::graph_container::CheckSpecifiersOptions { ext_overwrite: None, allow_unknown_media_types: true, }, ) .await?; emitter .cache_module_emits(&main_graph_container.graph()) .await?; print_install_report( &factory.sys(), started.elapsed(), &factory.install_reporter()?.unwrap().clone(), factory.workspace_resolver().await?, factory.npm_resolver().await?, ); Ok(()) } pub async fn install_command( flags: Arc<Flags>, install_flags: InstallFlags, ) -> Result<(), AnyError> { match install_flags { InstallFlags::Global(global_flags) => { Box::pin(global::install_global(flags, global_flags)).await } InstallFlags::Local(local_flags, _) => { if let InstallFlagsLocal::Add(add_flags) = &local_flags { local::check_if_installs_a_single_package_globally(Some(add_flags))?; } Box::pin(local::install_local(flags, local_flags)).await } } } pub fn print_install_report( sys: &dyn sys_traits::boxed::FsOpenBoxed, elapsed: std::time::Duration, install_reporter: &InstallReporter, workspace: &WorkspaceResolver<CliSys>, npm_resolver: &CliNpmResolver, ) { fn human_elapsed(elapsed: u128) -> String { display::human_elapsed_with_ms_limit(elapsed, 3_000) } let rep = install_reporter; if !rep.stats.intialized_npm.is_empty() || !rep.stats.downloaded_jsr.is_empty() { let total_installed = rep.stats.intialized_npm.len() + rep.stats.downloaded_jsr.len(); log::info!( "{} {} {} {} {}", deno_terminal::colors::gray("Installed"), total_installed, deno_terminal::colors::gray(format!( "package{}", if total_installed > 1 { "s" } else { "" }, )), deno_terminal::colors::gray("in"), human_elapsed(elapsed.as_millis()) ); let total_reused = rep.stats.reused_npm.get() + rep.stats.reused_jsr.len(); log::info!( "{} {} {}", deno_terminal::colors::gray("Reused"), total_reused, deno_terminal::colors::gray(format!( "package{} from cache", if total_reused == 1 { "" } else { "s" }, )), ); if total_reused > 0 { log::info!( "{}", deno_terminal::colors::yellow_bold("+".repeat(total_reused)) ); } let jsr_downloaded = rep.stats.downloaded_jsr.len(); log::info!( "{} {} {}", deno_terminal::colors::gray("Downloaded"), jsr_downloaded, deno_terminal::colors::gray(format!( "package{} from JSR", if jsr_downloaded == 1 { "" } else { "s" }, )), ); if jsr_downloaded > 0 { log::info!( "{}", deno_terminal::colors::green("+".repeat(jsr_downloaded)) ); } let npm_download = rep.stats.downloaded_npm.len(); log::info!( "{} {} {}", deno_terminal::colors::gray("Downloaded"), npm_download, deno_terminal::colors::gray(format!( "package{} from npm", if npm_download == 1 { "" } else { "s" }, )), ); if npm_download > 0 { log::info!("{}", deno_terminal::colors::green("+".repeat(npm_download))); } } let CategorizedInstalledDeps { normal_deps: installed_normal_deps, dev_deps: installed_dev_deps, } = categorize_installed_npm_deps(npm_resolver, workspace, install_reporter); if !installed_normal_deps.is_empty() || !rep.stats.downloaded_jsr.is_empty() { log::info!(""); log::info!("{}", deno_terminal::colors::cyan("Dependencies:")); let mut jsr_packages = rep .stats .downloaded_jsr .clone() .into_iter() .collect::<Vec<_>>(); jsr_packages.sort(); for pkg in jsr_packages { let (name, version) = pkg.rsplit_once("@").unwrap(); log::info!( "{} {}{} {}", deno_terminal::colors::green("+"), deno_terminal::colors::gray("jsr:"), name, deno_terminal::colors::gray(version) ); } for pkg in &installed_normal_deps { log::info!( "{} {}{} {}", deno_terminal::colors::green("+"), deno_terminal::colors::gray("npm:"), pkg.nv.name, deno_terminal::colors::gray(pkg.nv.version.to_string()) ); } log::info!(""); } if !installed_dev_deps.is_empty() { log::info!("{}", deno_terminal::colors::cyan("Dev dependencies:")); for pkg in &installed_dev_deps { log::info!( "{} {}{} {}", deno_terminal::colors::green("+"), deno_terminal::colors::gray("npm:"), pkg.nv.name, deno_terminal::colors::gray(pkg.nv.version.to_string()) ); } } let warnings = install_reporter.take_scripts_warnings(); for warning in warnings { log::warn!("{}", warning.into_message(sys)); } let deprecation_messages = install_reporter.take_deprecation_message(); for message in deprecation_messages { log::warn!("{}", message); } warn_on_engines_mismatch(workspace); } /// Emit a warning for each package.json in the workspace whose `engines.node` /// or `engines.deno` field is not satisfied by the current runtime. /// /// Modeled on `npm`/`yarn`'s default behavior: this is a warning, never an /// error. Engines other than `node` and `deno` are ignored — Deno has no way /// to verify them. fn warn_on_engines_mismatch(workspace: &WorkspaceResolver<CliSys>) { for pkg_json in workspace.package_jsons() { let mismatches = pkg_json.check_engines(DENO_VERSION, NODE_VERSION); if mismatches.is_empty() { continue; } let display_path = pkg_json.path.display(); for m in mismatches { let warning_label = deno_terminal::colors::yellow("Warning"); let required = deno_terminal::colors::cyan(&m.required); let actual = deno_terminal::colors::cyan(&m.actual); match m.kind { EngineMismatchKind::Unsatisfied => { log::warn!( "{warning_label} {display_path}: engines.{engine} \"{required}\" is not satisfied by current {engine} version {actual}", engine = m.engine, ); } EngineMismatchKind::InvalidRequirement => { log::warn!( "{warning_label} {display_path}: engines.{engine} value \"{required}\" is not a valid version range", engine = m.engine, ); } } } } }