// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::path::PathBuf; use std::sync::Arc; use deno_config::deno_json; use deno_config::deno_json::CompilerOptionTypesDeserializeError; use deno_config::deno_json::NodeModulesDirMode; use deno_config::workspace::JsrPackageConfig; use deno_config::workspace::ToMaybeJsxImportSourceConfigError; use deno_core::ModuleSpecifier; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::serde_json; use deno_error::JsErrorBox; use deno_error::JsErrorClass; use deno_graph::CheckJsOption; use deno_graph::FillFromLockfileOptions; use deno_graph::GraphKind; use deno_graph::JsrLoadError; use deno_graph::ModuleError; use deno_graph::ModuleErrorKind; use deno_graph::ModuleGraph; use deno_graph::ModuleGraphError; use deno_graph::ModuleLoadError; use deno_graph::ResolutionError; use deno_graph::WorkspaceFastCheckOption; use deno_graph::source::Loader; use deno_graph::source::ResolveError; use deno_npm_installer::PackageCaching; use deno_npm_installer::graph::NpmCachingStrategy; use deno_path_util::url_to_file_path; use deno_resolver::cache::ParsedSourceCache; use deno_resolver::deno_json::CompilerOptionsResolver; use deno_resolver::deno_json::JsxImportSourceConfigResolver; use deno_resolver::graph::EnhanceGraphErrorMode; use deno_resolver::graph::enhance_graph_error; use deno_resolver::graph::enhanced_integrity_error_message; use deno_resolver::graph::format_deno_graph_error; use deno_resolver::npm::DenoInNpmPackageChecker; use deno_runtime::deno_permissions::PermissionsContainer; use deno_semver::SmallStackString; use deno_semver::jsr::JsrDepPackageReq; use indexmap::IndexMap; use sys_traits::FsMetadata; use crate::args::CliLockfile; use crate::args::CliOptions; use crate::args::DenoSubcommand; use crate::args::config_to_deno_graph_workspace_member; use crate::args::jsr_url; use crate::cache; use crate::cache::GlobalHttpCache; use crate::cache::ModuleInfoCache; use crate::colors; use crate::file_fetcher::CliDenoGraphLoader; use crate::file_fetcher::CliFileFetcher; use crate::npm::CliNpmGraphResolver; use crate::npm::CliNpmInstaller; use crate::npm::CliNpmResolver; use crate::resolver::CliCjsTracker; use crate::resolver::CliResolver; use crate::sys::CliSys; use crate::type_checker::CheckError; use crate::type_checker::CheckOptions; use crate::type_checker::TypeChecker; use crate::util::file_watcher::WatcherCommunicator; use crate::util::fs::canonicalize_path; #[derive(Clone)] pub struct GraphValidOptions<'a> { pub check_js: CheckJsOption<'a>, pub kind: GraphKind, /// Whether to exit the process for integrity check errors such as /// lockfile checksum mismatches and JSR integrity failures. /// Otherwise, surfaces integrity errors as errors. pub exit_integrity_errors: bool, pub allow_unknown_media_types: bool, pub ignore_graph_errors: bool, pub allow_unknown_jsr_exports: bool, } /// Check if `roots` and their deps are available. Returns `Ok(())` if /// so. Returns `Err(_)` if there is a known module graph or resolution /// error statically reachable from `roots`. /// /// It is preferable to use this over using deno_graph's API directly /// because it will have enhanced error message information specifically /// for the CLI. pub fn graph_valid( graph: &ModuleGraph, sys: &CliSys, roots: &[ModuleSpecifier], options: GraphValidOptions, ) -> Result<(), JsErrorBox> { if options.exit_integrity_errors { graph_exit_integrity_errors(graph); } let mut errors = graph_walk_errors( graph, sys, roots, GraphWalkErrorsOptions { check_js: options.check_js, kind: options.kind, allow_unknown_media_types: options.allow_unknown_media_types, ignore_graph_errors: options.ignore_graph_errors, allow_unknown_jsr_exports: options.allow_unknown_jsr_exports, }, ); match errors.next() { Some(error) => Err(error), _ => { // finally surface the npm resolution result if let Err(err) = &graph.npm_dep_graph_result { return Err(JsErrorBox::new( err.get_class(), format_deno_graph_error(err), )); } Ok(()) } } } pub fn fill_graph_from_lockfile( graph: &mut ModuleGraph, lockfile: &deno_lockfile::Lockfile, ) { graph.fill_from_lockfile(FillFromLockfileOptions { redirects: lockfile .content .redirects .iter() .map(|(from, to)| (from.as_str(), to.as_str())), package_specifiers: lockfile .content .packages .specifiers .iter() .map(|(dep, id)| (dep, id.as_str())), }); } #[derive(Clone)] pub struct GraphWalkErrorsOptions<'a> { pub check_js: CheckJsOption<'a>, pub kind: GraphKind, pub ignore_graph_errors: bool, pub allow_unknown_media_types: bool, pub allow_unknown_jsr_exports: bool, } /// Walks the errors found in the module graph that should be surfaced to users /// and enhances them with CLI information. pub fn graph_walk_errors<'a>( graph: &'a ModuleGraph, sys: &'a CliSys, roots: &'a [ModuleSpecifier], options: GraphWalkErrorsOptions<'a>, ) -> impl Iterator + 'a { fn should_ignore_resolution_error_for_types(err: &ResolutionError) -> bool { match err { ResolutionError::ResolverError { error, .. } => match error.as_ref() { ResolveError::Specifier(_) => true, ResolveError::ImportMap(err) => matches!( err.as_kind(), import_map::ImportMapErrorKind::UnmappedBareSpecifier { .. } ), ResolveError::Other(_) => false, }, _ => false, } } fn should_ignore_module_graph_error_for_types( err: &ModuleGraphError, ) -> bool { match err { ModuleGraphError::ResolutionError(err) => { should_ignore_resolution_error_for_types(err) } ModuleGraphError::TypesResolutionError(err) => { should_ignore_resolution_error_for_types(err) } ModuleGraphError::ModuleError(module_error) => { matches!(module_error.as_kind(), ModuleErrorKind::Missing { .. }) } } } fn should_ignore_error( sys: &CliSys, graph_kind: GraphKind, allow_unknown_media_types: bool, ignore_graph_errors: bool, error: &ModuleGraphError, ) -> bool { if (graph_kind == GraphKind::TypesOnly || allow_unknown_media_types) && matches!( error.as_module_error_kind(), Some(ModuleErrorKind::UnsupportedMediaType { .. }) ) { return true; } if ignore_graph_errors && should_ignore_module_graph_error_for_types(error) { return true; } // surface these as typescript diagnostics instead graph_kind.include_types() && has_module_graph_error_for_tsc_diagnostic(sys, error) } graph .walk( roots.iter(), deno_graph::WalkOptions { check_js: options.check_js, kind: options.kind, follow_dynamic: false, prefer_fast_check_graph: false, }, ) .errors() .flat_map(move |error| { if should_ignore_error( sys, graph.graph_kind(), options.allow_unknown_media_types, options.ignore_graph_errors, &error, ) { log::debug!("Ignoring: {}", error); return None; } let is_root = match &error { ModuleGraphError::ResolutionError(_) | ModuleGraphError::TypesResolutionError(_) => false, ModuleGraphError::ModuleError(error) => { roots.contains(error.specifier()) } }; if is_root && options.allow_unknown_jsr_exports && matches!( error.as_module_error_kind(), Some(ModuleErrorKind::Load { err: ModuleLoadError::Jsr(JsrLoadError::UnknownExport { .. }), .. }) ) { return None; } let message = enhance_graph_error( sys, &error, if is_root { EnhanceGraphErrorMode::HideRange } else { EnhanceGraphErrorMode::ShowRange }, ); Some(JsErrorBox::new(error.get_class(), message)) }) } fn has_module_graph_error_for_tsc_diagnostic( sys: &CliSys, error: &ModuleGraphError, ) -> bool { match error { ModuleGraphError::ModuleError(error) => { module_error_for_tsc_diagnostic(sys, error).is_some() } ModuleGraphError::ResolutionError(error) => { resolution_error_for_tsc_diagnostic(error).is_some() } ModuleGraphError::TypesResolutionError(error) => { resolution_error_for_tsc_diagnostic(error).is_some() } } } pub struct ModuleNotFoundGraphErrorRef<'a> { pub specifier: &'a ModuleSpecifier, pub maybe_range: Option<&'a deno_graph::Range>, } pub fn module_error_for_tsc_diagnostic<'a>( sys: &CliSys, error: &'a ModuleError, ) -> Option> { match error.as_kind() { ModuleErrorKind::Missing { specifier, maybe_referrer, } => Some(ModuleNotFoundGraphErrorRef { specifier, maybe_range: maybe_referrer.as_ref(), }), ModuleErrorKind::Load { specifier, maybe_referrer, err: ModuleLoadError::Loader(_), } => { if let Ok(path) = deno_path_util::url_to_file_path(specifier) { if sys.fs_is_dir_no_err(path) { return Some(ModuleNotFoundGraphErrorRef { specifier, maybe_range: maybe_referrer.as_ref(), }); } } None } _ => None, } } pub struct ModuleNotFoundNodeResolutionErrorRef<'a> { pub specifier: &'a str, pub maybe_range: Option<&'a deno_graph::Range>, } pub fn resolution_error_for_tsc_diagnostic( error: &ResolutionError, ) -> Option { match error { ResolutionError::ResolverError { error, specifier, range, } => match error.as_ref() { ResolveError::Other(error) => { // would be nice if there were an easier way of doing this let text = error.to_string(); if text.contains("[ERR_MODULE_NOT_FOUND]") { Some(ModuleNotFoundNodeResolutionErrorRef { specifier, maybe_range: Some(range), }) } else { None } } _ => None, }, _ => None, } } pub fn graph_exit_integrity_errors(graph: &ModuleGraph) { for error in graph.module_errors() { exit_for_integrity_error(error); } } fn exit_for_integrity_error(err: &ModuleError) { if let Some(err_message) = enhanced_integrity_error_message(err) { log::error!("{} {}", colors::red("error:"), err_message); deno_runtime::exit(10); } } pub struct CreateGraphOptions<'a> { pub graph_kind: GraphKind, pub roots: Vec, pub is_dynamic: bool, /// Specify `None` to use the default CLI loader. pub loader: Option<&'a mut dyn Loader>, pub npm_caching: NpmCachingStrategy, } pub struct ModuleGraphCreator { options: Arc, module_graph_builder: Arc, type_checker: Arc, } impl ModuleGraphCreator { pub fn new( options: Arc, module_graph_builder: Arc, type_checker: Arc, ) -> Self { Self { options, module_graph_builder, type_checker, } } pub async fn create_graph( &self, graph_kind: GraphKind, roots: Vec, npm_caching: NpmCachingStrategy, ) -> Result { let mut cache = self .module_graph_builder .create_graph_loader_with_root_permissions(); self .create_graph_with_loader(graph_kind, roots, &mut cache, npm_caching) .await } pub async fn create_graph_with_loader( &self, graph_kind: GraphKind, roots: Vec, loader: &mut dyn Loader, npm_caching: NpmCachingStrategy, ) -> Result { self .create_graph_with_options(CreateGraphOptions { is_dynamic: false, graph_kind, roots, loader: Some(loader), npm_caching, }) .await } pub async fn create_and_validate_publish_graph( &self, package_configs: &[JsrPackageConfig], build_fast_check_graph: bool, ) -> Result { struct PublishLoader(CliDenoGraphLoader); impl Loader for PublishLoader { fn load( &self, specifier: &deno_ast::ModuleSpecifier, options: deno_graph::source::LoadOptions, ) -> deno_graph::source::LoadFuture { if matches!(specifier.scheme(), "bun" | "virtual" | "cloudflare") { Box::pin(std::future::ready(Ok(Some( deno_graph::source::LoadResponse::External { specifier: specifier.clone(), }, )))) } else if matches!(specifier.scheme(), "http" | "https") && !specifier.as_str().starts_with(jsr_url().as_str()) { // mark non-JSR remote modules as external so we don't need --allow-import // permissions as these will error out later when publishing Box::pin(std::future::ready(Ok(Some( deno_graph::source::LoadResponse::External { specifier: specifier.clone(), }, )))) } else { self.0.load(specifier, options) } } } fn graph_has_external_remote(graph: &ModuleGraph) -> bool { // Earlier on, we marked external non-JSR modules as external. // If the graph contains any of those, it would cause type checking // to crash, so since publishing is going to fail anyway, skip type // checking. graph.modules().any(|module| match module { deno_graph::Module::External(external_module) => { matches!(external_module.specifier.scheme(), "http" | "https") } _ => false, }) } let mut roots = Vec::new(); for package_config in package_configs { roots.extend(package_config.config_file.resolve_export_value_urls()?); } let loader = self .module_graph_builder .create_graph_loader_with_root_permissions(); let mut publish_loader = PublishLoader(loader); let mut graph = self .create_graph_with_options(CreateGraphOptions { is_dynamic: false, graph_kind: deno_graph::GraphKind::All, roots, loader: Some(&mut publish_loader), npm_caching: self.options.default_npm_caching_strategy(), }) .await?; self.graph_valid(&graph)?; if self.options.type_check_mode().is_true() && !graph_has_external_remote(&graph) { self.type_check_graph(graph.clone())?; } if build_fast_check_graph { let fast_check_workspace_members = package_configs .iter() .map(|p| config_to_deno_graph_workspace_member(&p.config_file)) .collect::, _>>()?; self.module_graph_builder.build_fast_check_graph( &mut graph, BuildFastCheckGraphOptions { workspace_fast_check: WorkspaceFastCheckOption::Enabled( &fast_check_workspace_members, ), }, )?; } Ok(graph) } pub async fn create_graph_with_options( &self, options: CreateGraphOptions<'_>, ) -> Result { let mut graph = ModuleGraph::new(options.graph_kind); self .module_graph_builder .build_graph_with_npm_resolution( &mut graph, BuildGraphWithNpmOptions { request: BuildGraphRequest::Roots(options.roots), is_dynamic: options.is_dynamic, loader: options.loader, npm_caching: options.npm_caching, }, ) .await?; Ok(graph) } pub async fn create_graph_and_maybe_check( &self, roots: Vec, ) -> Result, AnyError> { let graph_kind = self.options.type_check_mode().as_graph_kind(); let graph = self .create_graph_with_options(CreateGraphOptions { is_dynamic: false, graph_kind, roots, loader: None, npm_caching: self.options.default_npm_caching_strategy(), }) .await?; self.graph_valid(&graph)?; if self.options.type_check_mode().is_true() { // provide the graph to the type checker, then get it back after it's done let graph = self.type_check_graph(graph)?; Ok(graph) } else { Ok(Arc::new(graph)) } } pub fn graph_valid(&self, graph: &ModuleGraph) -> Result<(), JsErrorBox> { self.module_graph_builder.graph_valid(graph) } #[allow(clippy::result_large_err)] fn type_check_graph( &self, graph: ModuleGraph, ) -> Result, CheckError> { self.type_checker.check( graph, CheckOptions { build_fast_check_graph: true, lib: self.options.ts_type_lib_window(), reload: self.options.reload_flag(), type_check_mode: self.options.type_check_mode(), }, ) } } pub struct BuildFastCheckGraphOptions<'a> { /// Whether to do fast check on workspace members. This /// is mostly only useful when publishing. pub workspace_fast_check: deno_graph::WorkspaceFastCheckOption<'a>, } #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BuildGraphWithNpmResolutionError { #[class(inherit)] #[error(transparent)] CompilerOptionTypesDeserialize(#[from] CompilerOptionTypesDeserializeError), #[class(inherit)] #[error(transparent)] SerdeJson(#[from] serde_json::Error), #[class(inherit)] #[error(transparent)] ToMaybeJsxImportSourceConfig(#[from] ToMaybeJsxImportSourceConfigError), #[class(inherit)] #[error(transparent)] NodeModulesDirParse(#[from] deno_json::NodeModulesDirParseError), #[class(inherit)] #[error(transparent)] Other(#[from] JsErrorBox), #[class(generic)] #[error( "Resolving npm specifier entrypoints this way is currently not supported with \"nodeModules\": \"manual\". In the meantime, try with --node-modules-dir=auto instead" )] UnsupportedNpmSpecifierEntrypointResolutionWay, } pub enum BuildGraphRequest { Roots(Vec), Reload(Vec), } pub struct BuildGraphWithNpmOptions<'a> { pub request: BuildGraphRequest, pub is_dynamic: bool, /// Specify `None` to use the default CLI loader. pub loader: Option<&'a mut dyn Loader>, pub npm_caching: NpmCachingStrategy, } pub struct ModuleGraphBuilder { caches: Arc, cjs_tracker: Arc, cli_options: Arc, file_fetcher: Arc, global_http_cache: Arc, in_npm_pkg_checker: DenoInNpmPackageChecker, lockfile: Option>, maybe_file_watcher_reporter: Option, module_info_cache: Arc, npm_graph_resolver: Arc, npm_installer: Option>, npm_resolver: CliNpmResolver, parsed_source_cache: Arc, resolver: Arc, root_permissions_container: PermissionsContainer, sys: CliSys, compiler_options_resolver: Arc, } impl ModuleGraphBuilder { #[allow(clippy::too_many_arguments)] pub fn new( caches: Arc, cjs_tracker: Arc, cli_options: Arc, file_fetcher: Arc, global_http_cache: Arc, in_npm_pkg_checker: DenoInNpmPackageChecker, lockfile: Option>, maybe_file_watcher_reporter: Option, module_info_cache: Arc, npm_graph_resolver: Arc, npm_installer: Option>, npm_resolver: CliNpmResolver, parsed_source_cache: Arc, resolver: Arc, root_permissions_container: PermissionsContainer, sys: CliSys, compiler_options_resolver: Arc, ) -> Self { Self { caches, cjs_tracker, cli_options, file_fetcher, global_http_cache, in_npm_pkg_checker, lockfile, maybe_file_watcher_reporter, module_info_cache, npm_graph_resolver, npm_installer, npm_resolver, parsed_source_cache, resolver, root_permissions_container, sys, compiler_options_resolver, } } pub async fn build_graph_with_npm_resolution( &self, graph: &mut ModuleGraph, options: BuildGraphWithNpmOptions<'_>, ) -> Result<(), BuildGraphWithNpmResolutionError> { enum MutLoaderRef<'a> { Borrowed(&'a mut dyn Loader), Owned(CliDenoGraphLoader), } impl MutLoaderRef<'_> { pub fn as_mut_loader(&mut self) -> &mut dyn Loader { match self { Self::Borrowed(loader) => *loader, Self::Owned(loader) => loader, } } } let analyzer = self.module_info_cache.as_module_analyzer(); let mut loader = match options.loader { Some(loader) => MutLoaderRef::Borrowed(loader), None => { MutLoaderRef::Owned(self.create_graph_loader_with_root_permissions()) } }; let jsx_import_source_config_resolver = JsxImportSourceConfigResolver::from_compiler_options_resolver( &self.compiler_options_resolver, )?; let graph_resolver = self.resolver.as_graph_resolver( self.cjs_tracker.as_ref(), &jsx_import_source_config_resolver, ); let maybe_file_watcher_reporter = self .maybe_file_watcher_reporter .as_ref() .map(|r| r.as_reporter()); let mut locker = self.lockfile.as_ref().map(|l| l.as_deno_graph_locker()); self .build_graph_with_npm_resolution_and_build_options( graph, options.request, loader.as_mut_loader(), deno_graph::BuildOptions { skip_dynamic_deps: self.cli_options.unstable_lazy_dynamic_imports() && graph.graph_kind() == GraphKind::CodeOnly, is_dynamic: options.is_dynamic, passthrough_jsr_specifiers: false, executor: Default::default(), file_system: &self.sys, jsr_url_provider: &CliJsrUrlProvider, npm_resolver: Some(self.npm_graph_resolver.as_ref()), module_analyzer: &analyzer, module_info_cacher: self.module_info_cache.as_ref(), reporter: maybe_file_watcher_reporter, resolver: Some(&graph_resolver), locker: locker.as_mut().map(|l| l as _), unstable_bytes_imports: self.cli_options.unstable_raw_imports(), unstable_text_imports: self.cli_options.unstable_raw_imports(), }, options.npm_caching, ) .await?; if let Some(npm_installer) = &self.npm_installer { if graph.has_node_specifier && graph.graph_kind().include_types() { npm_installer.inject_synthetic_types_node_package().await?; } } Ok(()) } async fn build_graph_with_npm_resolution_and_build_options<'a>( &self, graph: &mut ModuleGraph, request: BuildGraphRequest, loader: &'a mut dyn deno_graph::source::Loader, options: deno_graph::BuildOptions<'a>, npm_caching: NpmCachingStrategy, ) -> Result<(), BuildGraphWithNpmResolutionError> { // ensure an "npm install" is done if the user has explicitly // opted into using a node_modules directory if self .cli_options .specified_node_modules_dir()? .map(|m| m == NodeModulesDirMode::Auto) .unwrap_or(false) { if let Some(npm_installer) = &self.npm_installer { let already_done = npm_installer .ensure_top_level_package_json_install() .await?; if !already_done && matches!(npm_caching, NpmCachingStrategy::Eager) { npm_installer.cache_packages(PackageCaching::All).await?; } } } // fill the graph with the information from the lockfile let is_first_execution = graph.roots.is_empty(); if is_first_execution { // populate the information from the lockfile if let Some(lockfile) = &self.lockfile { let lockfile = lockfile.lock(); fill_graph_from_lockfile(graph, &lockfile); } } let initial_redirects_len = graph.redirects.len(); let initial_package_deps_len = graph.packages.package_deps_sum(); let initial_package_mappings_len = graph.packages.mappings().len(); match request { BuildGraphRequest::Roots(roots) => { if roots.iter().any(|r| r.scheme() == "npm") && self.npm_resolver.is_byonm() { return Err(BuildGraphWithNpmResolutionError::UnsupportedNpmSpecifierEntrypointResolutionWay); } let imports = if graph.graph_kind().include_types() { // Resolve all the imports from every config file. We'll separate // them later based on the folder we're type checking. let mut imports_by_referrer = IndexMap::<_, Vec<_>>::with_capacity( self.compiler_options_resolver.size(), ); for (referrer, files) in self .compiler_options_resolver .ts_configs() .iter() .filter_map(|t| t.files()) { imports_by_referrer .entry(referrer) .or_default() .extend(files.iter().map(|f| f.relative_specifier.clone())); } for (referrer, types) in self .compiler_options_resolver .all() .flat_map(|d| d.compiler_options_types().as_ref()) { imports_by_referrer .entry(referrer) .or_default() .extend(types.clone()); } imports_by_referrer .into_iter() .map(|(referrer, imports)| deno_graph::ReferrerImports { referrer: referrer.clone(), imports, }) .collect() } else { Vec::new() }; graph.build(roots, imports, loader, options).await; } BuildGraphRequest::Reload(urls) => { graph.reload(urls, loader, options).await } } let has_redirects_changed = graph.redirects.len() != initial_redirects_len; let has_jsr_package_deps_changed = graph.packages.package_deps_sum() != initial_package_deps_len; let has_jsr_package_mappings_changed = graph.packages.mappings().len() != initial_package_mappings_len; if has_redirects_changed || has_jsr_package_deps_changed || has_jsr_package_mappings_changed { if let Some(lockfile) = &self.lockfile { let mut lockfile = lockfile.lock(); // https redirects if has_redirects_changed { let graph_redirects = graph.redirects.iter().filter(|(from, _)| { !matches!(from.scheme(), "npm" | "file" | "deno") }); for (from, to) in graph_redirects { lockfile.insert_redirect(from.to_string(), to.to_string()); } } // jsr package mappings if has_jsr_package_mappings_changed { for (from, to) in graph.packages.mappings() { lockfile.insert_package_specifier( JsrDepPackageReq::jsr(from.clone()), to.version.to_custom_string::(), ); } } // jsr packages if has_jsr_package_deps_changed { for (nv, deps) in graph.packages.packages_with_deps() { lockfile.add_package_deps(nv, deps.cloned()); } } } } Ok(()) } pub fn build_fast_check_graph( &self, graph: &mut ModuleGraph, options: BuildFastCheckGraphOptions, ) -> Result<(), ToMaybeJsxImportSourceConfigError> { if !graph.graph_kind().include_types() { return Ok(()); } log::debug!("Building fast check graph"); let fast_check_cache = if matches!( options.workspace_fast_check, deno_graph::WorkspaceFastCheckOption::Disabled ) { Some(cache::FastCheckCache::new(self.caches.fast_check_db())) } else { None }; let parser = self.parsed_source_cache.as_capturing_parser(); let jsx_import_source_config_resolver = JsxImportSourceConfigResolver::from_compiler_options_resolver( &self.compiler_options_resolver, )?; let graph_resolver = self.resolver.as_graph_resolver( self.cjs_tracker.as_ref(), &jsx_import_source_config_resolver, ); graph.build_fast_check_type_graph( deno_graph::BuildFastCheckTypeGraphOptions { es_parser: Some(&parser), fast_check_cache: fast_check_cache.as_ref().map(|c| c as _), fast_check_dts: false, jsr_url_provider: &CliJsrUrlProvider, resolver: Some(&graph_resolver), workspace_fast_check: options.workspace_fast_check, }, ); Ok(()) } /// Creates the default loader used for creating a graph. pub fn create_graph_loader_with_root_permissions( &self, ) -> CliDenoGraphLoader { self.create_graph_loader_with_permissions( self.root_permissions_container.clone(), ) } pub fn create_graph_loader_with_permissions( &self, permissions: PermissionsContainer, ) -> CliDenoGraphLoader { CliDenoGraphLoader::new( self.file_fetcher.clone(), self.global_http_cache.clone(), self.in_npm_pkg_checker.clone(), self.sys.clone(), deno_resolver::file_fetcher::DenoGraphLoaderOptions { file_header_overrides: self.cli_options.resolve_file_header_overrides(), permissions: Some(permissions), }, ) } /// Check if `roots` and their deps are available. Returns `Ok(())` if /// so. Returns `Err(_)` if there is a known module graph or resolution /// error statically reachable from `roots` and not a dynamic import. pub fn graph_valid(&self, graph: &ModuleGraph) -> Result<(), JsErrorBox> { self.graph_roots_valid( graph, &graph.roots.iter().cloned().collect::>(), false, false, ) } pub fn graph_roots_valid( &self, graph: &ModuleGraph, roots: &[ModuleSpecifier], allow_unknown_media_types: bool, allow_unknown_jsr_exports: bool, ) -> Result<(), JsErrorBox> { graph_valid( graph, &self.sys, roots, GraphValidOptions { kind: if self.cli_options.type_check_mode().is_true() { GraphKind::All } else { GraphKind::CodeOnly }, check_js: CheckJsOption::Custom( self.compiler_options_resolver.as_ref(), ), exit_integrity_errors: true, allow_unknown_media_types, ignore_graph_errors: matches!( self.cli_options.sub_command(), DenoSubcommand::Check { .. } ), allow_unknown_jsr_exports, }, ) } } /// Gets if any of the specified root's "file:" dependents are in the /// provided changed set. pub fn has_graph_root_local_dependent_changed( graph: &ModuleGraph, root: &ModuleSpecifier, canonicalized_changed_paths: &HashSet, ) -> bool { let mut dependent_specifiers = graph.walk( std::iter::once(root), deno_graph::WalkOptions { follow_dynamic: true, kind: GraphKind::All, prefer_fast_check_graph: true, check_js: CheckJsOption::True, }, ); while let Some((s, _)) = dependent_specifiers.next() { if let Ok(path) = url_to_file_path(s) { if let Ok(path) = canonicalize_path(&path) { if canonicalized_changed_paths.contains(&path) { return true; } } } else { // skip walking this remote module's dependencies dependent_specifiers.skip_previous_dependencies(); } } false } #[derive(Clone, Debug)] pub struct FileWatcherReporter { watcher_communicator: Arc, file_paths: Arc>>, } impl FileWatcherReporter { pub fn new(watcher_communicator: Arc) -> Self { Self { watcher_communicator, file_paths: Default::default(), } } pub fn as_reporter(&self) -> &dyn deno_graph::source::Reporter { self } } impl deno_graph::source::Reporter for FileWatcherReporter { fn on_load( &self, specifier: &ModuleSpecifier, modules_done: usize, modules_total: usize, ) { let mut file_paths = self.file_paths.lock(); if specifier.scheme() == "file" { // Don't trust that the path is a valid path at this point: // https://github.com/denoland/deno/issues/26209. if let Ok(file_path) = specifier.to_file_path() { file_paths.push(file_path); } } if modules_done == modules_total { self .watcher_communicator .watch_paths(file_paths.drain(..).collect()) .unwrap(); } } } #[derive(Debug, Default, Clone, Copy)] pub struct CliJsrUrlProvider; impl deno_graph::source::JsrUrlProvider for CliJsrUrlProvider { fn url(&self) -> &'static ModuleSpecifier { jsr_url() } }