2023-01-02 16:00:42 -05:00
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2021-12-16 05:45:41 -05:00
2023-02-09 22:00:23 -05:00
use crate ::args ::CliOptions ;
2022-12-06 14:12:51 -05:00
use crate ::args ::Lockfile ;
2023-02-24 14:42:45 -05:00
use crate ::args ::TsTypeLib ;
2022-12-09 09:40:48 -05:00
use crate ::cache ;
2023-08-01 20:49:09 -04:00
use crate ::cache ::GlobalHttpCache ;
2023-10-25 18:13:22 -04:00
use crate ::cache ::ModuleInfoCache ;
2023-04-14 16:22:33 -04:00
use crate ::cache ::ParsedSourceCache ;
2021-12-16 05:45:41 -05:00
use crate ::colors ;
use crate ::errors ::get_error_class_name ;
2023-04-14 16:22:33 -04:00
use crate ::file_fetcher ::FileFetcher ;
2023-04-21 21:02:46 -04:00
use crate ::npm ::CliNpmResolver ;
2023-02-15 11:30:54 -05:00
use crate ::resolver ::CliGraphResolver ;
2023-12-07 15:59:13 -05:00
use crate ::resolver ::SloppyImportsResolution ;
use crate ::resolver ::SloppyImportsResolver ;
2022-12-09 09:40:48 -05:00
use crate ::tools ::check ;
2023-04-14 18:05:46 -04:00
use crate ::tools ::check ::TypeChecker ;
2023-10-19 01:05:00 -04:00
use crate ::util ::file_watcher ::WatcherCommunicator ;
2023-09-13 17:53:07 -04:00
use crate ::util ::sync ::TaskQueue ;
use crate ::util ::sync ::TaskQueuePermit ;
2022-06-28 16:45:55 -04:00
2023-12-07 15:59:13 -05:00
use deno_ast ::MediaType ;
2022-12-09 09:40:48 -05:00
use deno_core ::anyhow ::bail ;
2023-11-16 20:28:38 -05:00
use deno_core ::anyhow ::Context ;
2021-12-16 05:45:41 -05:00
use deno_core ::error ::custom_error ;
use deno_core ::error ::AnyError ;
2023-04-14 16:22:33 -04:00
use deno_core ::parking_lot ::Mutex ;
2023-02-24 14:42:45 -05:00
use deno_core ::parking_lot ::RwLock ;
2021-12-16 05:45:41 -05:00
use deno_core ::ModuleSpecifier ;
2023-04-14 16:22:33 -04:00
use deno_graph ::source ::Loader ;
2023-10-20 00:02:08 -04:00
use deno_graph ::source ::ResolveError ;
2023-06-06 17:07:46 -04:00
use deno_graph ::GraphKind ;
2023-02-22 14:15:25 -05:00
use deno_graph ::Module ;
2023-10-25 18:13:22 -04:00
use deno_graph ::ModuleAnalyzer ;
2023-03-21 11:46:40 -04:00
use deno_graph ::ModuleError ;
2021-12-16 05:45:41 -05:00
use deno_graph ::ModuleGraph ;
use deno_graph ::ModuleGraphError ;
2023-01-24 15:14:49 -05:00
use deno_graph ::ResolutionError ;
use deno_graph ::SpecifierError ;
2023-12-01 15:12:10 -05:00
use deno_runtime ::deno_fs ::FileSystem ;
2023-04-21 21:02:46 -04:00
use deno_runtime ::deno_node ;
2023-01-07 11:25:34 -05:00
use deno_runtime ::permissions ::PermissionsContainer ;
2023-09-18 10:46:44 -04:00
use deno_semver ::package ::PackageNv ;
use deno_semver ::package ::PackageReq ;
2023-01-27 17:36:23 -05:00
use import_map ::ImportMapError ;
2023-02-24 14:42:45 -05:00
use std ::collections ::HashMap ;
use std ::collections ::HashSet ;
2023-06-14 18:29:19 -04:00
use std ::path ::PathBuf ;
2021-12-16 05:45:41 -05:00
use std ::sync ::Arc ;
2023-02-15 11:30:54 -05:00
#[ derive(Clone, Copy) ]
pub struct GraphValidOptions {
pub check_js : bool ,
pub follow_type_only : bool ,
pub is_vendoring : bool ,
}
2023-02-09 22:00:23 -05:00
/// 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_with_cli_options (
graph : & ModuleGraph ,
2023-12-07 15:59:13 -05:00
fs : & Arc < dyn FileSystem > ,
2023-02-09 22:00:23 -05:00
roots : & [ ModuleSpecifier ] ,
options : & CliOptions ,
) -> Result < ( ) , AnyError > {
graph_valid (
graph ,
2023-12-07 15:59:13 -05:00
fs ,
2023-02-09 22:00:23 -05:00
roots ,
2023-02-15 11:30:54 -05:00
GraphValidOptions {
is_vendoring : false ,
2023-06-07 10:09:10 -04:00
follow_type_only : options . type_check_mode ( ) . is_true ( ) ,
2023-02-09 22:00:23 -05:00
check_js : options . check_js ( ) ,
} ,
)
2021-12-16 05:45:41 -05:00
}
2023-02-09 22:00:23 -05:00
/// 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 ,
2023-12-07 15:59:13 -05:00
fs : & Arc < dyn FileSystem > ,
2023-02-09 22:00:23 -05:00
roots : & [ ModuleSpecifier ] ,
2023-02-15 11:30:54 -05:00
options : GraphValidOptions ,
2023-02-09 22:00:23 -05:00
) -> Result < ( ) , AnyError > {
2023-02-15 11:30:54 -05:00
let mut errors = graph
. walk (
roots ,
deno_graph ::WalkOptions {
check_js : options . check_js ,
follow_type_only : options . follow_type_only ,
follow_dynamic : options . is_vendoring ,
} ,
)
. errors ( )
. flat_map ( | error | {
let is_root = match & error {
2023-10-24 17:43:19 -04:00
ModuleGraphError ::ResolutionError ( _ )
| ModuleGraphError ::TypesResolutionError ( _ ) = > false ,
2023-03-21 11:46:40 -04:00
ModuleGraphError ::ModuleError ( error ) = > {
roots . contains ( error . specifier ( ) )
}
2023-02-15 11:30:54 -05:00
} ;
2023-10-24 17:43:19 -04:00
let mut message = match & error {
ModuleGraphError ::ResolutionError ( resolution_error ) = > {
enhanced_resolution_error_message ( resolution_error )
}
ModuleGraphError ::TypesResolutionError ( resolution_error ) = > {
format! (
" Failed resolving types. {} " ,
2023-12-07 15:59:13 -05:00
enhanced_resolution_error_message ( resolution_error )
2023-10-24 17:43:19 -04:00
)
}
2023-12-07 15:59:13 -05:00
ModuleGraphError ::ModuleError ( e ) = > {
enhanced_module_error_message ( fs , e )
}
2023-02-15 11:30:54 -05:00
} ;
2021-12-16 05:45:41 -05:00
2023-02-15 11:30:54 -05:00
if let Some ( range ) = error . maybe_range ( ) {
if ! is_root & & ! range . specifier . as_str ( ) . contains ( " /$deno$eval " ) {
2023-12-01 15:12:10 -05:00
message . push_str ( " \n at " ) ;
message . push_str ( & format_range_with_colors ( range ) ) ;
2023-02-15 11:30:54 -05:00
}
2021-12-16 05:45:41 -05:00
}
2023-02-15 11:30:54 -05:00
if options . is_vendoring {
// warn about failing dynamic imports when vendoring, but don't fail completely
2023-03-21 11:46:40 -04:00
if matches! (
error ,
ModuleGraphError ::ModuleError ( ModuleError ::MissingDynamic ( _ , _ ) )
) {
2023-02-15 11:30:54 -05:00
log ::warn! ( " Ignoring: {:#} " , message ) ;
return None ;
}
// ignore invalid downgrades and invalid local imports when vendoring
2023-10-24 17:43:19 -04:00
match & error {
ModuleGraphError ::ResolutionError ( err )
| ModuleGraphError ::TypesResolutionError ( err ) = > {
if matches! (
err ,
ResolutionError ::InvalidDowngrade { .. }
| ResolutionError ::InvalidLocalImport { .. }
) {
return None ;
}
2023-02-15 11:30:54 -05:00
}
2023-10-24 17:43:19 -04:00
ModuleGraphError ::ModuleError ( _ ) = > { }
2023-02-15 11:30:54 -05:00
}
}
Some ( custom_error ( get_error_class_name ( & error . into ( ) ) , message ) )
} ) ;
if let Some ( error ) = errors . next ( ) {
Err ( error )
} else {
Ok ( ( ) )
}
2021-12-16 05:45:41 -05:00
}
2022-12-06 14:12:51 -05:00
/// Checks the lockfile against the graph and and exits on errors.
pub fn graph_lock_or_exit ( graph : & ModuleGraph , lockfile : & mut Lockfile ) {
for module in graph . modules ( ) {
2023-02-22 14:15:25 -05:00
let source = match module {
2023-06-26 15:53:17 -04:00
Module ::Esm ( module ) if module . media_type . is_declaration ( ) = > continue , // skip declaration files
2023-02-22 14:15:25 -05:00
Module ::Esm ( module ) = > & module . source ,
Module ::Json ( module ) = > & module . source ,
Module ::Node ( _ ) | Module ::Npm ( _ ) | Module ::External ( _ ) = > continue ,
} ;
if ! lockfile . check_or_insert_remote ( module . specifier ( ) . as_str ( ) , source ) {
let err = format! (
concat! (
" The source code is invalid, as it does not match the expected hash in the lock file. \n " ,
" Specifier: {} \n " ,
" Lock file: {} " ,
) ,
module . specifier ( ) ,
lockfile . filename . display ( ) ,
) ;
log ::error! ( " {} {} " , colors ::red ( " error: " ) , err ) ;
std ::process ::exit ( 10 ) ;
2022-12-06 14:12:51 -05:00
}
2021-12-16 05:45:41 -05:00
}
}
2022-12-09 09:40:48 -05:00
2023-10-25 18:13:22 -04:00
pub struct CreateGraphOptions < ' a > {
pub graph_kind : GraphKind ,
pub roots : Vec < ModuleSpecifier > ,
pub loader : & ' a mut dyn Loader ,
pub analyzer : & ' a dyn ModuleAnalyzer ,
}
2023-04-14 16:22:33 -04:00
pub struct ModuleGraphBuilder {
options : Arc < CliOptions > ,
2023-12-01 15:12:10 -05:00
fs : Arc < dyn FileSystem > ,
2023-04-14 16:22:33 -04:00
resolver : Arc < CliGraphResolver > ,
2023-09-29 09:26:25 -04:00
npm_resolver : Arc < dyn CliNpmResolver > ,
2023-10-25 18:13:22 -04:00
module_info_cache : Arc < ModuleInfoCache > ,
2023-04-14 16:22:33 -04:00
parsed_source_cache : Arc < ParsedSourceCache > ,
lockfile : Option < Arc < Mutex < Lockfile > > > ,
2023-06-14 18:29:19 -04:00
maybe_file_watcher_reporter : Option < FileWatcherReporter > ,
2023-04-14 16:22:33 -04:00
emit_cache : cache ::EmitCache ,
file_fetcher : Arc < FileFetcher > ,
2023-08-01 20:49:09 -04:00
global_http_cache : Arc < GlobalHttpCache > ,
2023-04-14 18:05:46 -04:00
type_checker : Arc < TypeChecker > ,
2023-04-14 16:22:33 -04:00
}
2023-02-22 14:15:25 -05:00
2023-04-14 16:22:33 -04:00
impl ModuleGraphBuilder {
#[ allow(clippy::too_many_arguments) ]
pub fn new (
options : Arc < CliOptions > ,
2023-12-01 15:12:10 -05:00
fs : Arc < dyn FileSystem > ,
2023-04-14 16:22:33 -04:00
resolver : Arc < CliGraphResolver > ,
2023-09-29 09:26:25 -04:00
npm_resolver : Arc < dyn CliNpmResolver > ,
2023-10-25 18:13:22 -04:00
module_info_cache : Arc < ModuleInfoCache > ,
2023-04-14 16:22:33 -04:00
parsed_source_cache : Arc < ParsedSourceCache > ,
lockfile : Option < Arc < Mutex < Lockfile > > > ,
2023-06-14 18:29:19 -04:00
maybe_file_watcher_reporter : Option < FileWatcherReporter > ,
2023-04-14 16:22:33 -04:00
emit_cache : cache ::EmitCache ,
file_fetcher : Arc < FileFetcher > ,
2023-08-01 20:49:09 -04:00
global_http_cache : Arc < GlobalHttpCache > ,
2023-04-14 18:05:46 -04:00
type_checker : Arc < TypeChecker > ,
2023-04-14 16:22:33 -04:00
) -> Self {
Self {
options ,
2023-12-01 15:12:10 -05:00
fs ,
2023-04-14 16:22:33 -04:00
resolver ,
npm_resolver ,
2023-10-25 18:13:22 -04:00
module_info_cache ,
2023-04-14 16:22:33 -04:00
parsed_source_cache ,
lockfile ,
2023-06-14 18:29:19 -04:00
maybe_file_watcher_reporter ,
2023-04-14 16:22:33 -04:00
emit_cache ,
file_fetcher ,
2023-08-01 20:49:09 -04:00
global_http_cache ,
2023-04-14 18:05:46 -04:00
type_checker ,
2023-04-14 16:22:33 -04:00
}
2022-12-09 09:40:48 -05:00
}
2023-10-25 18:13:22 -04:00
pub async fn create_graph (
& self ,
graph_kind : GraphKind ,
roots : Vec < ModuleSpecifier > ,
) -> Result < deno_graph ::ModuleGraph , AnyError > {
let mut cache = self . create_graph_loader ( ) ;
self
. create_graph_with_loader ( graph_kind , roots , & mut cache )
. await
}
2023-04-14 16:22:33 -04:00
pub async fn create_graph_with_loader (
& self ,
2023-06-06 17:07:46 -04:00
graph_kind : GraphKind ,
2023-04-14 16:22:33 -04:00
roots : Vec < ModuleSpecifier > ,
loader : & mut dyn Loader ,
2023-10-25 18:13:22 -04:00
) -> Result < deno_graph ::ModuleGraph , AnyError > {
let store = self . parsed_source_cache . as_store ( ) ;
let analyzer = self . module_info_cache . as_module_analyzer ( None , & * store ) ;
self
. create_graph_with_options ( CreateGraphOptions {
graph_kind ,
roots ,
loader ,
analyzer : & analyzer ,
} )
. await
}
pub async fn create_graph_with_options (
& self ,
options : CreateGraphOptions < '_ > ,
2023-04-14 16:22:33 -04:00
) -> Result < deno_graph ::ModuleGraph , AnyError > {
let maybe_imports = self . options . to_maybe_imports ( ) ? ;
2023-11-16 20:28:38 -05:00
let maybe_workspace_config = self . options . maybe_workspace_config ( ) ;
let workspace_members = if let Some ( wc ) = maybe_workspace_config {
workspace_config_to_workspace_members ( wc ) ?
} else {
vec! [ ]
} ;
2023-04-14 16:22:33 -04:00
let cli_resolver = self . resolver . clone ( ) ;
let graph_resolver = cli_resolver . as_graph_resolver ( ) ;
let graph_npm_resolver = cli_resolver . as_graph_npm_resolver ( ) ;
2023-06-14 18:29:19 -04:00
let maybe_file_watcher_reporter = self
. maybe_file_watcher_reporter
. as_ref ( )
. map ( | r | r . as_reporter ( ) ) ;
2023-04-14 16:22:33 -04:00
2023-10-25 18:13:22 -04:00
let mut graph = ModuleGraph ::new ( options . graph_kind ) ;
2023-04-14 16:22:33 -04:00
self
. build_graph_with_npm_resolution (
& mut graph ,
2023-10-25 18:13:22 -04:00
options . roots ,
options . loader ,
2023-04-14 16:22:33 -04:00
deno_graph ::BuildOptions {
is_dynamic : false ,
imports : maybe_imports ,
resolver : Some ( graph_resolver ) ,
2023-12-01 15:12:10 -05:00
file_system : Some ( & DenoGraphFsAdapter ( self . fs . as_ref ( ) ) ) ,
2023-04-14 16:22:33 -04:00
npm_resolver : Some ( graph_npm_resolver ) ,
2023-10-25 18:13:22 -04:00
module_analyzer : Some ( options . analyzer ) ,
2023-06-14 18:29:19 -04:00
reporter : maybe_file_watcher_reporter ,
2023-11-16 20:28:38 -05:00
workspace_members ,
2023-04-14 16:22:33 -04:00
} ,
)
. await ? ;
2023-09-29 09:26:25 -04:00
if let Some ( npm_resolver ) = self . npm_resolver . as_managed ( ) {
if graph . has_node_specifier & & self . options . type_check_mode ( ) . is_true ( ) {
npm_resolver . inject_synthetic_types_node_package ( ) . await ? ;
}
2023-01-24 09:05:54 -05:00
}
2023-04-14 16:22:33 -04:00
Ok ( graph )
}
pub async fn create_graph_and_maybe_check (
& self ,
roots : Vec < ModuleSpecifier > ,
) -> Result < Arc < deno_graph ::ModuleGraph > , AnyError > {
let mut cache = self . create_graph_loader ( ) ;
let maybe_imports = self . options . to_maybe_imports ( ) ? ;
2023-11-16 20:28:38 -05:00
let maybe_workspace_config = self . options . maybe_workspace_config ( ) ;
let workspace_members = if let Some ( wc ) = maybe_workspace_config {
workspace_config_to_workspace_members ( wc ) ?
} else {
vec! [ ]
} ;
2023-04-14 16:22:33 -04:00
let cli_resolver = self . resolver . clone ( ) ;
let graph_resolver = cli_resolver . as_graph_resolver ( ) ;
let graph_npm_resolver = cli_resolver . as_graph_npm_resolver ( ) ;
2023-10-25 18:13:22 -04:00
let store = self . parsed_source_cache . as_store ( ) ;
let analyzer = self . module_info_cache . as_module_analyzer ( None , & * store ) ;
2023-06-07 10:09:10 -04:00
let graph_kind = self . options . type_check_mode ( ) . as_graph_kind ( ) ;
2023-06-06 17:07:46 -04:00
let mut graph = ModuleGraph ::new ( graph_kind ) ;
2023-06-14 18:29:19 -04:00
let maybe_file_watcher_reporter = self
. maybe_file_watcher_reporter
. as_ref ( )
. map ( | r | r . as_reporter ( ) ) ;
2023-04-14 16:22:33 -04:00
self
. build_graph_with_npm_resolution (
& mut graph ,
roots ,
& mut cache ,
deno_graph ::BuildOptions {
is_dynamic : false ,
imports : maybe_imports ,
2023-12-01 15:12:10 -05:00
file_system : Some ( & DenoGraphFsAdapter ( self . fs . as_ref ( ) ) ) ,
2023-04-14 16:22:33 -04:00
resolver : Some ( graph_resolver ) ,
npm_resolver : Some ( graph_npm_resolver ) ,
2023-10-25 18:13:22 -04:00
module_analyzer : Some ( & analyzer ) ,
2023-06-14 18:29:19 -04:00
reporter : maybe_file_watcher_reporter ,
2023-11-16 20:28:38 -05:00
workspace_members ,
2023-04-14 16:22:33 -04:00
} ,
)
. await ? ;
let graph = Arc ::new ( graph ) ;
2023-12-07 15:59:13 -05:00
graph_valid_with_cli_options (
& graph ,
& self . fs ,
& graph . roots ,
& self . options ,
) ? ;
2023-04-14 16:22:33 -04:00
if let Some ( lockfile ) = & self . lockfile {
graph_lock_or_exit ( & graph , & mut lockfile . lock ( ) ) ;
2022-12-09 09:40:48 -05:00
}
2023-04-14 16:22:33 -04:00
2023-06-07 10:09:10 -04:00
if self . options . type_check_mode ( ) . is_true ( ) {
2023-04-14 18:05:46 -04:00
self
. type_checker
. check (
graph . clone ( ) ,
check ::CheckOptions {
2023-04-14 16:22:33 -04:00
lib : self . options . ts_type_lib_window ( ) ,
2023-04-14 18:05:46 -04:00
log_ignored_options : true ,
reload : self . options . reload_flag ( ) ,
} ,
)
. await ? ;
2022-12-09 09:40:48 -05:00
}
2023-04-14 16:22:33 -04:00
Ok ( graph )
2022-12-09 09:40:48 -05:00
}
2023-04-14 16:22:33 -04:00
pub async fn build_graph_with_npm_resolution < ' a > (
& self ,
graph : & mut ModuleGraph ,
roots : Vec < ModuleSpecifier > ,
loader : & mut dyn deno_graph ::source ::Loader ,
options : deno_graph ::BuildOptions < ' a > ,
) -> Result < ( ) , AnyError > {
2023-05-23 18:51:48 -04:00
// ensure an "npm install" is done if the user has explicitly
// opted into using a node_modules directory
if self . options . node_modules_dir_enablement ( ) = = Some ( true ) {
2023-09-30 12:06:38 -04:00
if let Some ( npm_resolver ) = self . npm_resolver . as_managed ( ) {
npm_resolver . ensure_top_level_package_json_install ( ) . await ? ;
}
2023-05-23 18:51:48 -04:00
}
2023-08-29 13:03:02 -04:00
// add the lockfile redirects to the graph if it's the first time executing
if graph . redirects . is_empty ( ) {
if let Some ( lockfile ) = & self . lockfile {
let lockfile = lockfile . lock ( ) ;
for ( from , to ) in & lockfile . content . redirects {
if let Ok ( from ) = ModuleSpecifier ::parse ( from ) {
if let Ok ( to ) = ModuleSpecifier ::parse ( to ) {
2023-09-18 10:46:44 -04:00
if ! matches! ( from . scheme ( ) , " file " | " npm " | " jsr " ) {
2023-08-29 13:03:02 -04:00
graph . redirects . insert ( from , to ) ;
}
}
}
}
}
}
2023-09-18 10:46:44 -04:00
// add the jsr specifiers to the graph if it's the first time executing
if graph . packages . is_empty ( ) {
if let Some ( lockfile ) = & self . lockfile {
let lockfile = lockfile . lock ( ) ;
for ( key , value ) in & lockfile . content . packages . specifiers {
if let Some ( key ) = key
. strip_prefix ( " jsr: " )
. and_then ( | key | PackageReq ::from_str ( key ) . ok ( ) )
{
if let Some ( value ) = value
. strip_prefix ( " jsr: " )
. and_then ( | value | PackageNv ::from_str ( value ) . ok ( ) )
{
graph . packages . add ( key , value ) ;
}
}
}
}
}
2023-09-07 09:09:16 -04:00
2023-04-14 16:22:33 -04:00
graph . build ( roots , loader , options ) . await ;
2022-12-09 09:40:48 -05:00
2023-08-29 13:03:02 -04:00
// add the redirects in the graph to the lockfile
if ! graph . redirects . is_empty ( ) {
if let Some ( lockfile ) = & self . lockfile {
2023-09-07 09:09:16 -04:00
let graph_redirects = graph . redirects . iter ( ) . filter ( | ( from , _ ) | {
! matches! ( from . scheme ( ) , " npm " | " file " | " deno " )
} ) ;
2023-08-29 13:03:02 -04:00
let mut lockfile = lockfile . lock ( ) ;
for ( from , to ) in graph_redirects {
lockfile . insert_redirect ( from . to_string ( ) , to . to_string ( ) ) ;
}
}
}
2023-09-18 10:46:44 -04:00
// add the jsr specifiers in the graph to the lockfile
if ! graph . packages . is_empty ( ) {
if let Some ( lockfile ) = & self . lockfile {
let mappings = graph . packages . mappings ( ) ;
let mut lockfile = lockfile . lock ( ) ;
for ( from , to ) in mappings {
lockfile . insert_package_specifier (
format! ( " jsr: {} " , from ) ,
format! ( " jsr: {} " , to ) ,
) ;
}
}
}
2023-09-07 09:09:16 -04:00
2023-09-29 09:26:25 -04:00
if let Some ( npm_resolver ) = self . npm_resolver . as_managed ( ) {
// ensure that the top level package.json is installed if a
// specifier was matched in the package.json
2023-09-30 12:06:38 -04:00
if self . resolver . found_package_json_dep ( ) {
npm_resolver . ensure_top_level_package_json_install ( ) . await ? ;
}
2023-02-22 14:15:25 -05:00
2023-09-29 09:26:25 -04:00
// resolve the dependencies of any pending dependencies
// that were inserted by building the graph
npm_resolver . resolve_pending ( ) . await ? ;
}
2023-04-11 18:10:51 -04:00
2023-04-14 16:22:33 -04:00
Ok ( ( ) )
}
2023-02-22 14:15:25 -05:00
2023-04-14 16:22:33 -04:00
/// Creates the default loader used for creating a graph.
pub fn create_graph_loader ( & self ) -> cache ::FetchCacher {
2023-04-26 16:23:28 -04:00
self . create_fetch_cacher ( PermissionsContainer ::allow_all ( ) )
2023-04-14 16:22:33 -04:00
}
pub fn create_fetch_cacher (
& self ,
2023-04-26 16:23:28 -04:00
permissions : PermissionsContainer ,
2023-04-14 16:22:33 -04:00
) -> cache ::FetchCacher {
cache ::FetchCacher ::new (
self . emit_cache . clone ( ) ,
self . file_fetcher . clone ( ) ,
self . options . resolve_file_header_overrides ( ) ,
2023-08-01 20:49:09 -04:00
self . global_http_cache . clone ( ) ,
2023-10-25 14:39:00 -04:00
self . npm_resolver . clone ( ) ,
2023-10-25 18:13:22 -04:00
self . module_info_cache . clone ( ) ,
2023-04-26 16:23:28 -04:00
permissions ,
2023-04-14 16:22:33 -04:00
)
}
2023-02-22 14:15:25 -05:00
}
2022-12-09 09:40:48 -05:00
pub fn error_for_any_npm_specifier (
2023-02-22 14:15:25 -05:00
graph : & ModuleGraph ,
2022-12-09 09:40:48 -05:00
) -> Result < ( ) , AnyError > {
2023-02-22 14:15:25 -05:00
for module in graph . modules ( ) {
match module {
Module ::Npm ( module ) = > {
2023-05-10 20:06:59 -04:00
bail! ( " npm specifiers have not yet been implemented for this subcommand (https://github.com/denoland/deno/issues/15960). Found: {} " , module . specifier )
2022-12-09 09:40:48 -05:00
}
2023-02-22 14:15:25 -05:00
Module ::Node ( module ) = > {
2023-05-10 20:06:59 -04:00
bail! ( " Node specifiers have not yet been implemented for this subcommand (https://github.com/denoland/deno/issues/15960). Found: node:{} " , module . module_name )
2023-02-22 14:15:25 -05:00
}
Module ::Esm ( _ ) | Module ::Json ( _ ) | Module ::External ( _ ) = > { }
}
2022-12-09 09:40:48 -05:00
}
2023-02-22 14:15:25 -05:00
Ok ( ( ) )
2022-12-09 09:40:48 -05:00
}
2023-01-24 15:14:49 -05:00
/// Adds more explanatory information to a resolution error.
pub fn enhanced_resolution_error_message ( error : & ResolutionError ) -> String {
2023-01-27 10:43:16 -05:00
let mut message = format! ( " {error} " ) ;
2023-01-24 15:14:49 -05:00
2023-01-27 17:36:23 -05:00
if let Some ( specifier ) = get_resolution_error_bare_node_specifier ( error ) {
message . push_str ( & format! (
" \n If you want to use a built-in Node module, add a \" node: \" prefix (ex. \" node:{specifier} \" ). "
) ) ;
}
message
}
2023-12-07 15:59:13 -05:00
pub fn enhanced_module_error_message (
fs : & Arc < dyn FileSystem > ,
error : & ModuleError ,
) -> String {
let additional_message = match error {
ModuleError ::Missing ( specifier , _ ) = > {
maybe_sloppy_imports_suggestion_message ( fs , specifier )
}
_ = > None ,
} ;
if let Some ( message ) = additional_message {
format! (
" {} {} or run with --unstable-sloppy-imports " ,
error , message
)
} else {
format! ( " {} " , error )
}
}
pub fn maybe_sloppy_imports_suggestion_message (
fs : & Arc < dyn FileSystem > ,
original_specifier : & ModuleSpecifier ,
) -> Option < String > {
let sloppy_imports_resolver = SloppyImportsResolver ::new ( fs . clone ( ) ) ;
let resolution = sloppy_imports_resolver . resolve ( original_specifier ) ;
sloppy_import_resolution_to_suggestion_message ( & resolution )
}
fn sloppy_import_resolution_to_suggestion_message (
resolution : & SloppyImportsResolution ,
) -> Option < String > {
match resolution {
SloppyImportsResolution ::None ( _ ) = > None ,
SloppyImportsResolution ::JsToTs ( specifier ) = > {
let media_type = MediaType ::from_specifier ( specifier ) ;
Some ( format! (
" Maybe change the extension to '{}' " ,
media_type . as_ts_extension ( )
) )
}
SloppyImportsResolution ::NoExtension ( specifier ) = > {
let media_type = MediaType ::from_specifier ( specifier ) ;
Some ( format! (
" Maybe add a '{}' extension " ,
media_type . as_ts_extension ( )
) )
}
SloppyImportsResolution ::Directory ( specifier ) = > {
let file_name = specifier
. path ( )
. rsplit_once ( '/' )
. map ( | ( _ , file_name ) | file_name )
. unwrap_or ( specifier . path ( ) ) ;
Some ( format! (
" Maybe specify path to '{}' file in directory instead " ,
file_name
) )
}
}
}
2023-01-27 17:36:23 -05:00
pub fn get_resolution_error_bare_node_specifier (
error : & ResolutionError ,
) -> Option < & str > {
2023-05-28 14:44:41 -04:00
get_resolution_error_bare_specifier ( error )
. filter ( | specifier | deno_node ::is_builtin_node_module ( specifier ) )
2023-01-27 17:36:23 -05:00
}
fn get_resolution_error_bare_specifier (
error : & ResolutionError ,
) -> Option < & str > {
2023-01-24 15:14:49 -05:00
if let ResolutionError ::InvalidSpecifier {
error : SpecifierError ::ImportPrefixMissing ( specifier , _ ) ,
..
} = error
{
2023-01-27 17:36:23 -05:00
Some ( specifier . as_str ( ) )
} else if let ResolutionError ::ResolverError { error , .. } = error {
2023-10-20 00:02:08 -04:00
if let ResolveError ::Other ( error ) = ( * error ) . as_ref ( ) {
if let Some ( ImportMapError ::UnmappedBareSpecifier ( specifier , _ ) ) =
error . downcast_ref ::< ImportMapError > ( )
{
Some ( specifier . as_str ( ) )
} else {
None
}
2023-01-27 17:36:23 -05:00
} else {
None
2023-01-24 15:14:49 -05:00
}
2023-01-27 17:36:23 -05:00
} else {
None
2023-01-24 15:14:49 -05:00
}
2023-01-27 17:36:23 -05:00
}
2023-01-24 15:14:49 -05:00
2023-06-06 17:07:46 -04:00
#[ derive(Debug) ]
2023-02-24 14:42:45 -05:00
struct GraphData {
graph : Arc < ModuleGraph > ,
checked_libs : HashMap < TsTypeLib , HashSet < ModuleSpecifier > > ,
}
/// Holds the `ModuleGraph` and what parts of it are type checked.
pub struct ModuleGraphContainer {
2023-03-04 20:07:11 -05:00
// Allow only one request to update the graph data at a time,
// but allow other requests to read from it at any time even
// while another request is updating the data.
update_queue : Arc < TaskQueue > ,
2023-02-24 14:42:45 -05:00
graph_data : Arc < RwLock < GraphData > > ,
}
impl ModuleGraphContainer {
2023-06-06 17:07:46 -04:00
pub fn new ( graph_kind : GraphKind ) -> Self {
Self {
update_queue : Default ::default ( ) ,
graph_data : Arc ::new ( RwLock ::new ( GraphData {
graph : Arc ::new ( ModuleGraph ::new ( graph_kind ) ) ,
checked_libs : Default ::default ( ) ,
} ) ) ,
}
}
2023-02-24 14:42:45 -05:00
/// Acquires a permit to modify the module graph without other code
/// having the chance to modify it. In the meantime, other code may
/// still read from the existing module graph.
pub async fn acquire_update_permit ( & self ) -> ModuleGraphUpdatePermit {
2023-03-04 20:07:11 -05:00
let permit = self . update_queue . acquire ( ) . await ;
2023-02-24 14:42:45 -05:00
ModuleGraphUpdatePermit {
permit ,
graph_data : self . graph_data . clone ( ) ,
graph : ( * self . graph_data . read ( ) . graph ) . clone ( ) ,
}
}
pub fn graph ( & self ) -> Arc < ModuleGraph > {
self . graph_data . read ( ) . graph . clone ( )
}
/// Mark `roots` and all of their dependencies as type checked under `lib`.
/// Assumes that all of those modules are known.
pub fn set_type_checked ( & self , roots : & [ ModuleSpecifier ] , lib : TsTypeLib ) {
// It's ok to analyze and update this while the module graph itself is
// being updated in a permit because the module graph update is always
// additive and this will be a subset of the original graph
let graph = self . graph ( ) ;
let entries = graph . walk (
roots ,
deno_graph ::WalkOptions {
check_js : true ,
follow_dynamic : true ,
follow_type_only : true ,
} ,
) ;
// now update
let mut data = self . graph_data . write ( ) ;
let checked_lib_set = data . checked_libs . entry ( lib ) . or_default ( ) ;
for ( specifier , _ ) in entries {
checked_lib_set . insert ( specifier . clone ( ) ) ;
}
}
/// Check if `roots` are all marked as type checked under `lib`.
pub fn is_type_checked (
& self ,
roots : & [ ModuleSpecifier ] ,
lib : TsTypeLib ,
) -> bool {
let data = self . graph_data . read ( ) ;
match data . checked_libs . get ( & lib ) {
Some ( checked_lib_set ) = > roots . iter ( ) . all ( | r | {
let found = data . graph . resolve ( r ) ;
checked_lib_set . contains ( & found )
} ) ,
None = > false ,
}
}
}
2023-06-14 18:29:19 -04:00
/// 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 ,
changed_specifiers : & HashSet < ModuleSpecifier > ,
) -> bool {
let roots = vec! [ root . clone ( ) ] ;
let mut dependent_specifiers = graph . walk (
& roots ,
deno_graph ::WalkOptions {
follow_dynamic : true ,
follow_type_only : true ,
check_js : true ,
} ,
) ;
while let Some ( ( s , _ ) ) = dependent_specifiers . next ( ) {
if s . scheme ( ) ! = " file " {
// skip walking this remote module's dependencies
dependent_specifiers . skip_previous_dependencies ( ) ;
} else if changed_specifiers . contains ( s ) {
return true ;
}
}
false
}
2023-02-24 14:42:45 -05:00
/// A permit for updating the module graph. When complete and
/// everything looks fine, calling `.commit()` will store the
/// new graph in the ModuleGraphContainer.
pub struct ModuleGraphUpdatePermit < ' a > {
2023-03-04 20:07:11 -05:00
permit : TaskQueuePermit < ' a > ,
2023-02-24 14:42:45 -05:00
graph_data : Arc < RwLock < GraphData > > ,
graph : ModuleGraph ,
}
impl < ' a > ModuleGraphUpdatePermit < ' a > {
/// Gets the module graph for mutation.
pub fn graph_mut ( & mut self ) -> & mut ModuleGraph {
& mut self . graph
}
/// Saves the mutated module graph in the container
/// and returns an Arc to the new module graph.
pub fn commit ( self ) -> Arc < ModuleGraph > {
let graph = Arc ::new ( self . graph ) ;
self . graph_data . write ( ) . graph = graph . clone ( ) ;
drop ( self . permit ) ; // explicit drop for clarity
graph
}
}
2023-06-14 18:29:19 -04:00
#[ derive(Clone, Debug) ]
pub struct FileWatcherReporter {
2023-10-30 20:25:58 -04:00
watcher_communicator : Arc < WatcherCommunicator > ,
2023-06-14 18:29:19 -04:00
file_paths : Arc < Mutex < Vec < PathBuf > > > ,
}
impl FileWatcherReporter {
2023-10-30 20:25:58 -04:00
pub fn new ( watcher_communicator : Arc < WatcherCommunicator > ) -> Self {
2023-06-14 18:29:19 -04:00
Self {
2023-10-19 01:05:00 -04:00
watcher_communicator ,
2023-06-14 18:29:19 -04:00
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 " {
file_paths . push ( specifier . to_file_path ( ) . unwrap ( ) ) ;
}
if modules_done = = modules_total {
2023-10-19 01:05:00 -04:00
self
. watcher_communicator
. watch_paths ( file_paths . drain ( .. ) . collect ( ) )
. unwrap ( ) ;
2023-06-14 18:29:19 -04:00
}
}
}
2023-11-16 20:28:38 -05:00
pub fn workspace_config_to_workspace_members (
workspace_config : & deno_config ::WorkspaceConfig ,
) -> Result < Vec < deno_graph ::WorkspaceMember > , AnyError > {
workspace_config
. members
. iter ( )
. map ( | member | {
workspace_member_config_try_into_workspace_member ( member ) . with_context (
| | {
format! (
" Failed to resolve configuration for '{}' workspace member at '{}' " ,
member . member_name ,
member . config_file . specifier . as_str ( )
)
} ,
)
} )
. collect ( )
}
fn workspace_member_config_try_into_workspace_member (
config : & deno_config ::WorkspaceMemberConfig ,
) -> Result < deno_graph ::WorkspaceMember , AnyError > {
let nv = deno_semver ::package ::PackageNv {
name : config . package_name . clone ( ) ,
version : deno_semver ::Version ::parse_standard ( & config . package_version ) ? ,
} ;
Ok ( deno_graph ::WorkspaceMember {
base : ModuleSpecifier ::from_directory_path ( & config . path ) . unwrap ( ) ,
nv ,
exports : config
. config_file
. to_exports_config ( ) ?
. into_map ( )
// todo(dsherret): deno_graph should use an IndexMap
. into_iter ( )
. collect ( ) ,
} )
}
2023-12-01 15:12:10 -05:00
pub struct DenoGraphFsAdapter < ' a > (
pub & ' a dyn deno_runtime ::deno_fs ::FileSystem ,
) ;
impl < ' a > deno_graph ::source ::FileSystem for DenoGraphFsAdapter < ' a > {
fn read_dir (
& self ,
dir_url : & deno_graph ::ModuleSpecifier ,
) -> Vec < deno_graph ::source ::DirEntry > {
use deno_core ::anyhow ;
use deno_graph ::source ::DirEntry ;
use deno_graph ::source ::DirEntryKind ;
let dir_path = match dir_url . to_file_path ( ) {
Ok ( path ) = > path ,
// ignore, treat as non-analyzable
Err ( ( ) ) = > return vec! [ ] ,
} ;
let entries = match self . 0. read_dir_sync ( & dir_path ) {
Ok ( dir ) = > dir ,
Err ( err )
if matches! (
err . kind ( ) ,
std ::io ::ErrorKind ::PermissionDenied | std ::io ::ErrorKind ::NotFound
) = >
{
return vec! [ ] ;
}
Err ( err ) = > {
return vec! [ DirEntry {
kind : DirEntryKind ::Error (
anyhow ::Error ::from ( err )
. context ( " Failed to read directory. " . to_string ( ) ) ,
) ,
url : dir_url . clone ( ) ,
} ] ;
}
} ;
let mut dir_entries = Vec ::with_capacity ( entries . len ( ) ) ;
for entry in entries {
let entry_path = dir_path . join ( & entry . name ) ;
dir_entries . push ( if entry . is_directory {
DirEntry {
kind : DirEntryKind ::Dir ,
url : ModuleSpecifier ::from_directory_path ( & entry_path ) . unwrap ( ) ,
}
} else if entry . is_file {
DirEntry {
kind : DirEntryKind ::File ,
url : ModuleSpecifier ::from_file_path ( & entry_path ) . unwrap ( ) ,
}
} else if entry . is_symlink {
DirEntry {
kind : DirEntryKind ::Symlink ,
url : ModuleSpecifier ::from_file_path ( & entry_path ) . unwrap ( ) ,
}
} else {
continue ;
} ) ;
}
dir_entries
}
}
pub fn format_range_with_colors ( range : & deno_graph ::Range ) -> String {
format! (
" {}:{}:{} " ,
colors ::cyan ( range . specifier . as_str ( ) ) ,
colors ::yellow ( & ( range . start . line + 1 ) . to_string ( ) ) ,
colors ::yellow ( & ( range . start . character + 1 ) . to_string ( ) )
)
}
2023-01-27 17:36:23 -05:00
#[ cfg(test) ]
mod test {
use std ::sync ::Arc ;
use deno_ast ::ModuleSpecifier ;
2023-10-20 00:02:08 -04:00
use deno_graph ::source ::ResolveError ;
2023-01-27 17:36:23 -05:00
use deno_graph ::Position ;
use deno_graph ::Range ;
use deno_graph ::ResolutionError ;
use deno_graph ::SpecifierError ;
2023-12-06 19:03:18 -05:00
use super ::* ;
2023-01-27 17:36:23 -05:00
#[ test ]
fn import_map_node_resolution_error ( ) {
let cases = vec! [ ( " fs " , Some ( " fs " ) ) , ( " other " , None ) ] ;
for ( input , output ) in cases {
let import_map = import_map ::ImportMap ::new (
ModuleSpecifier ::parse ( " file:///deno.json " ) . unwrap ( ) ,
) ;
let specifier = ModuleSpecifier ::parse ( " file:///file.ts " ) . unwrap ( ) ;
let err = import_map . resolve ( input , & specifier ) . err ( ) . unwrap ( ) ;
let err = ResolutionError ::ResolverError {
2023-10-20 00:02:08 -04:00
error : Arc ::new ( ResolveError ::Other ( err . into ( ) ) ) ,
2023-01-27 17:36:23 -05:00
specifier : input . to_string ( ) ,
range : Range {
specifier ,
start : Position ::zeroed ( ) ,
end : Position ::zeroed ( ) ,
} ,
} ;
assert_eq! ( get_resolution_error_bare_node_specifier ( & err ) , output ) ;
}
}
#[ test ]
fn bare_specifier_node_resolution_error ( ) {
let cases = vec! [ ( " process " , Some ( " process " ) ) , ( " other " , None ) ] ;
for ( input , output ) in cases {
let specifier = ModuleSpecifier ::parse ( " file:///file.ts " ) . unwrap ( ) ;
let err = ResolutionError ::InvalidSpecifier {
range : Range {
specifier ,
start : Position ::zeroed ( ) ,
end : Position ::zeroed ( ) ,
} ,
error : SpecifierError ::ImportPrefixMissing ( input . to_string ( ) , None ) ,
} ;
assert_eq! ( get_resolution_error_bare_node_specifier ( & err ) , output , ) ;
}
}
2023-12-07 15:59:13 -05:00
#[ test ]
fn test_sloppy_import_resolution_to_message ( ) {
// none
let url = ModuleSpecifier ::parse ( " file:///dir/index.js " ) . unwrap ( ) ;
assert_eq! (
sloppy_import_resolution_to_suggestion_message (
& SloppyImportsResolution ::None ( & url )
) ,
None ,
) ;
// directory
assert_eq! (
sloppy_import_resolution_to_suggestion_message (
& SloppyImportsResolution ::Directory (
ModuleSpecifier ::parse ( " file:///dir/index.js " ) . unwrap ( )
)
)
. unwrap ( ) ,
" Maybe specify path to 'index.js' file in directory instead "
) ;
// no ext
assert_eq! (
sloppy_import_resolution_to_suggestion_message (
& SloppyImportsResolution ::NoExtension (
ModuleSpecifier ::parse ( " file:///dir/index.mjs " ) . unwrap ( )
)
)
. unwrap ( ) ,
" Maybe add a '.mjs' extension "
) ;
// js to ts
assert_eq! (
sloppy_import_resolution_to_suggestion_message (
& SloppyImportsResolution ::JsToTs (
ModuleSpecifier ::parse ( " file:///dir/index.mts " ) . unwrap ( )
)
)
. unwrap ( ) ,
" Maybe change the extension to '.mts' "
) ;
}
2023-01-24 15:14:49 -05:00
}