2021-01-11 12:13:41 -05:00
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
2020-09-05 20:34:02 -04:00
2021-10-10 17:26:22 -04:00
use crate ::cache ;
2021-08-09 10:53:21 -04:00
use crate ::colors ;
2021-10-06 13:07:04 -04:00
use crate ::compat ;
2021-10-18 13:36:28 -04:00
use crate ::compat ::NodeEsmResolver ;
2021-05-10 12:16:39 -04:00
use crate ::config_file ::ConfigFile ;
2021-11-08 20:26:39 -05:00
use crate ::config_file ::MaybeImportsResult ;
2019-11-04 10:38:52 -05:00
use crate ::deno_dir ;
2021-10-10 17:26:22 -04:00
use crate ::emit ;
use crate ::errors ::get_module_graph_error_class ;
2020-11-05 19:38:21 -05:00
use crate ::file_fetcher ::CacheSetting ;
use crate ::file_fetcher ::FileFetcher ;
2019-11-04 10:38:52 -05:00
use crate ::flags ;
2020-02-19 08:17:13 -05:00
use crate ::http_cache ;
2021-10-10 17:26:22 -04:00
use crate ::lockfile ::as_maybe_locker ;
2019-11-04 10:38:52 -05:00
use crate ::lockfile ::Lockfile ;
2021-10-10 17:26:22 -04:00
use crate ::resolver ::ImportMapResolver ;
2021-11-08 20:26:39 -05:00
use crate ::resolver ::JsxResolver ;
2020-10-22 20:50:15 -04:00
use crate ::source_maps ::SourceMapGetter ;
2021-01-07 21:08:51 -05:00
use crate ::version ;
2020-10-22 20:50:15 -04:00
2020-12-15 00:52:55 -05:00
use deno_core ::error ::anyhow ;
2021-10-10 17:26:22 -04:00
use deno_core ::error ::custom_error ;
2020-12-15 00:52:55 -05:00
use deno_core ::error ::get_custom_error_class ;
2020-09-14 12:48:57 -04:00
use deno_core ::error ::AnyError ;
2021-01-04 18:15:52 -05:00
use deno_core ::error ::Context ;
2021-07-06 23:48:01 -04:00
use deno_core ::parking_lot ::Mutex ;
2021-02-17 13:47:18 -05:00
use deno_core ::resolve_url ;
2020-10-22 20:50:15 -04:00
use deno_core ::url ::Url ;
2021-09-29 04:47:24 -04:00
use deno_core ::CompiledWasmModuleStore ;
2020-12-15 00:52:55 -05:00
use deno_core ::ModuleSource ;
2020-01-05 11:56:18 -05:00
use deno_core ::ModuleSpecifier ;
2021-09-10 21:38:24 -04:00
use deno_core ::SharedArrayBufferStore ;
use deno_runtime ::deno_broadcast_channel ::InMemoryBroadcastChannel ;
use deno_runtime ::deno_web ::BlobStore ;
use deno_runtime ::inspector_server ::InspectorServer ;
use deno_runtime ::permissions ::Permissions ;
2021-08-07 08:49:38 -04:00
use deno_tls ::rustls ::RootCertStore ;
use deno_tls ::rustls_native_certs ::load_native_certs ;
use deno_tls ::webpki_roots ::TLS_SERVER_ROOTS ;
2021-09-10 21:38:24 -04:00
use import_map ::ImportMap ;
2020-12-15 00:52:55 -05:00
use std ::collections ::HashMap ;
2021-06-19 10:14:43 -04:00
use std ::collections ::HashSet ;
2019-11-04 10:38:52 -05:00
use std ::env ;
2021-08-07 08:49:38 -04:00
use std ::fs ::File ;
use std ::io ::BufReader ;
2021-09-24 11:10:42 -04:00
use std ::ops ::Deref ;
2019-11-04 10:38:52 -05:00
use std ::sync ::Arc ;
/// This structure represents state of single "deno" program.
///
/// It is shared by all created workers (thus V8 isolates).
2021-09-24 11:10:42 -04:00
#[ derive(Clone) ]
pub struct ProcState ( Arc < Inner > ) ;
2021-10-19 10:01:46 -04:00
#[ derive(Default) ]
struct GraphData {
modules : HashMap < ModuleSpecifier , Result < ModuleSource , AnyError > > ,
// because the graph detects resolution issues early, but is build and dropped
// during the `prepare_module_load` method, we need to extract out the module
// resolution map so that those errors can be surfaced at the appropriate time
resolution_map :
HashMap < ModuleSpecifier , HashMap < String , deno_graph ::Resolved > > ,
2021-10-21 10:18:18 -04:00
// in some cases we want to provide the range where the resolution error
2021-10-19 10:01:46 -04:00
// occurred but need to surface it on load, but on load we don't know who the
// referrer and span was, so we need to cache those
2021-10-21 10:18:18 -04:00
resolved_map : HashMap < ModuleSpecifier , deno_graph ::Range > ,
2021-10-19 10:01:46 -04:00
// deno_graph detects all sorts of issues at build time (prepare_module_load)
// but if they are errors at that stage, the don't cause the correct behaviors
// so we cache the error and then surface it when appropriate (e.g. load)
maybe_graph_error : Option < deno_graph ::ModuleGraphError > ,
}
2021-09-24 11:10:42 -04:00
pub struct Inner {
2019-11-04 10:38:52 -05:00
/// Flags parsed from `argv` contents.
2020-02-26 05:52:15 -05:00
pub flags : flags ::Flags ,
2019-11-04 10:38:52 -05:00
pub dir : deno_dir ::DenoDir ,
2020-12-21 08:04:25 -05:00
pub coverage_dir : Option < String > ,
2020-11-05 19:38:21 -05:00
pub file_fetcher : FileFetcher ,
2021-10-19 10:01:46 -04:00
graph_data : Arc < Mutex < GraphData > > ,
2020-10-23 17:01:54 -04:00
pub lockfile : Option < Arc < Mutex < Lockfile > > > ,
2021-05-10 12:16:39 -04:00
pub maybe_config_file : Option < ConfigFile > ,
2021-11-08 20:26:39 -05:00
pub maybe_import_map : Option < Arc < ImportMap > > ,
2020-09-25 04:24:51 -04:00
pub maybe_inspector_server : Option < Arc < InspectorServer > > ,
2021-08-07 08:49:38 -04:00
pub root_cert_store : Option < RootCertStore > ,
2021-07-05 09:34:37 -04:00
pub blob_store : BlobStore ,
2021-05-22 12:08:24 -04:00
pub broadcast_channel : InMemoryBroadcastChannel ,
2021-07-06 13:42:52 -04:00
pub shared_array_buffer_store : SharedArrayBufferStore ,
2021-09-29 04:47:24 -04:00
pub compiled_wasm_module_store : CompiledWasmModuleStore ,
2019-11-04 10:38:52 -05:00
}
2021-09-24 11:10:42 -04:00
impl Deref for ProcState {
type Target = Arc < Inner > ;
fn deref ( & self ) -> & Self ::Target {
& self . 0
}
}
impl ProcState {
pub async fn build ( flags : flags ::Flags ) -> Result < Self , AnyError > {
2021-07-27 17:25:09 -04:00
let maybe_custom_root = flags
. cache_path
. clone ( )
. or_else ( | | env ::var ( " DENO_DIR " ) . map ( String ::into ) . ok ( ) ) ;
let dir = deno_dir ::DenoDir ::new ( maybe_custom_root ) ? ;
2020-02-19 08:17:13 -05:00
let deps_cache_location = dir . root . join ( " deps " ) ;
2020-05-07 08:32:57 -04:00
let http_cache = http_cache ::HttpCache ::new ( & deps_cache_location ) ;
2021-08-07 08:49:38 -04:00
let mut root_cert_store = RootCertStore ::empty ( ) ;
let ca_stores : Vec < String > = flags
. ca_stores
. clone ( )
. or_else ( | | {
let env_ca_store = env ::var ( " DENO_TLS_CA_STORE " ) . ok ( ) ? ;
Some (
env_ca_store
. split ( ',' )
. map ( | s | s . trim ( ) . to_string ( ) )
. filter ( | s | ! s . is_empty ( ) )
. collect ( ) ,
)
} )
. unwrap_or_else ( | | vec! [ " mozilla " . to_string ( ) ] ) ;
for store in ca_stores . iter ( ) {
match store . as_str ( ) {
" mozilla " = > {
root_cert_store . add_server_trust_anchors ( & TLS_SERVER_ROOTS ) ;
}
" system " = > {
let roots = load_native_certs ( )
. expect ( " could not load platform certs " )
. roots ;
root_cert_store . roots . extend ( roots ) ;
}
_ = > {
return Err ( anyhow! ( " Unknown certificate store \" {} \" specified (allowed: \" system,mozilla \" ) " , store ) ) ;
}
}
}
2021-01-04 18:15:52 -05:00
let ca_file = flags . ca_file . clone ( ) . or_else ( | | env ::var ( " DENO_CERT " ) . ok ( ) ) ;
2021-08-07 08:49:38 -04:00
if let Some ( ca_file ) = ca_file {
let certfile = File ::open ( & ca_file ) ? ;
let mut reader = BufReader ::new ( certfile ) ;
// This function does not return specific errors, if it fails give a generic message.
if let Err ( _err ) = root_cert_store . add_pem_file ( & mut reader ) {
return Err ( anyhow! ( " Unable to add pem file to certificate store " ) ) ;
}
}
2019-11-04 10:38:52 -05:00
2021-08-09 10:53:21 -04:00
if let Some ( insecure_allowlist ) =
2021-08-10 07:19:45 -04:00
flags . unsafely_ignore_certificate_errors . as_ref ( )
2021-08-09 10:53:21 -04:00
{
let domains = if insecure_allowlist . is_empty ( ) {
2021-08-10 07:19:45 -04:00
" for all hostnames " . to_string ( )
2021-08-09 10:53:21 -04:00
} else {
format! ( " for: {} " , insecure_allowlist . join ( " , " ) )
} ;
2021-08-10 12:47:44 -04:00
let msg =
format! ( " DANGER: TLS certificate validation is disabled {} " , domains ) ;
2021-08-09 10:53:21 -04:00
eprintln! ( " {} " , colors ::yellow ( msg ) ) ;
}
2020-11-05 19:38:21 -05:00
let cache_usage = if flags . cached_only {
CacheSetting ::Only
} else if ! flags . cache_blocklist . is_empty ( ) {
CacheSetting ::ReloadSome ( flags . cache_blocklist . clone ( ) )
} else if flags . reload {
CacheSetting ::ReloadAll
} else {
CacheSetting ::Use
} ;
2021-07-05 09:34:37 -04:00
let blob_store = BlobStore ::default ( ) ;
2021-05-22 12:08:24 -04:00
let broadcast_channel = InMemoryBroadcastChannel ::default ( ) ;
2021-07-06 13:42:52 -04:00
let shared_array_buffer_store = SharedArrayBufferStore ::default ( ) ;
2021-09-29 04:47:24 -04:00
let compiled_wasm_module_store = CompiledWasmModuleStore ::default ( ) ;
2021-04-07 09:22:14 -04:00
2020-11-05 19:38:21 -05:00
let file_fetcher = FileFetcher ::new (
2020-02-19 08:17:13 -05:00
http_cache ,
2020-11-05 19:38:21 -05:00
cache_usage ,
! flags . no_remote ,
2021-08-07 08:49:38 -04:00
Some ( root_cert_store . clone ( ) ) ,
2021-07-05 09:34:37 -04:00
blob_store . clone ( ) ,
2021-08-10 07:19:45 -04:00
flags . unsafely_ignore_certificate_errors . clone ( ) ,
2019-11-04 10:38:52 -05:00
) ? ;
let lockfile = if let Some ( filename ) = & flags . lock {
2020-09-24 18:31:17 -04:00
let lockfile = Lockfile ::new ( filename . clone ( ) , flags . lock_write ) ? ;
2020-10-23 17:01:54 -04:00
Some ( Arc ::new ( Mutex ::new ( lockfile ) ) )
2019-11-04 10:38:52 -05:00
} else {
None
} ;
2021-05-10 12:16:39 -04:00
let maybe_config_file =
if let Some ( config_path ) = flags . config_path . as_ref ( ) {
Some ( ConfigFile ::read ( config_path ) ? )
} else {
None
} ;
2021-11-08 20:26:39 -05:00
let maybe_import_map : Option < Arc < ImportMap > > =
2020-05-29 10:32:15 -04:00
match flags . import_map_path . as_ref ( ) {
None = > None ,
2021-02-17 08:32:57 -05:00
Some ( import_map_url ) = > {
let import_map_specifier =
2021-07-30 09:03:41 -04:00
deno_core ::resolve_url_or_path ( import_map_url ) . context ( format! (
" Bad URL ( \" {} \" ) for import map. " ,
import_map_url
) ) ? ;
2021-02-17 08:32:57 -05:00
let file = file_fetcher
2021-04-11 22:15:43 -04:00
. fetch ( & import_map_specifier , & mut Permissions ::allow_all ( ) )
2021-05-04 08:27:20 -04:00
. await
. context ( format! (
" Unable to load '{}' import map " ,
import_map_specifier
) ) ? ;
2021-02-17 08:32:57 -05:00
let import_map =
ImportMap ::from_json ( import_map_specifier . as_str ( ) , & file . source ) ? ;
2021-11-08 20:26:39 -05:00
Some ( Arc ::new ( import_map ) )
2020-05-29 10:32:15 -04:00
}
} ;
2020-09-25 04:24:51 -04:00
let maybe_inspect_host = flags . inspect . or ( flags . inspect_brk ) ;
2021-03-25 14:17:37 -04:00
let maybe_inspector_server = maybe_inspect_host . map ( | host | {
Arc ::new ( InspectorServer ::new ( host , version ::get_user_agent ( ) ) )
} ) ;
2020-09-25 04:24:51 -04:00
2020-12-21 08:04:25 -05:00
let coverage_dir = flags
. coverage_dir
. clone ( )
. or_else ( | | env ::var ( " DENO_UNSTABLE_COVERAGE_DIR " ) . ok ( ) ) ;
2021-09-24 11:10:42 -04:00
Ok ( ProcState ( Arc ::new ( Inner {
2019-11-04 10:38:52 -05:00
dir ,
2020-12-21 08:04:25 -05:00
coverage_dir ,
2019-11-04 10:38:52 -05:00
flags ,
file_fetcher ,
2021-10-19 10:01:46 -04:00
graph_data : Default ::default ( ) ,
2019-11-04 10:38:52 -05:00
lockfile ,
2021-05-10 12:16:39 -04:00
maybe_config_file ,
2020-05-29 10:32:15 -04:00
maybe_import_map ,
2020-09-25 04:24:51 -04:00
maybe_inspector_server ,
2021-08-07 08:49:38 -04:00
root_cert_store : Some ( root_cert_store . clone ( ) ) ,
2021-07-05 09:34:37 -04:00
blob_store ,
2021-05-22 12:08:24 -04:00
broadcast_channel ,
2021-07-06 13:42:52 -04:00
shared_array_buffer_store ,
2021-09-29 04:47:24 -04:00
compiled_wasm_module_store ,
2021-09-24 11:10:42 -04:00
} ) ) )
2019-11-04 10:38:52 -05:00
}
2021-10-19 10:01:46 -04:00
pub ( crate ) fn take_graph_error (
& self ,
) -> Option < deno_graph ::ModuleGraphError > {
self . graph_data . lock ( ) . maybe_graph_error . take ( )
}
2021-10-10 17:26:22 -04:00
/// Return any imports that should be brought into the scope of the module
/// graph.
2021-11-08 20:26:39 -05:00
fn get_maybe_imports ( & self ) -> MaybeImportsResult {
2021-10-10 17:26:22 -04:00
let mut imports = Vec ::new ( ) ;
if let Some ( config_file ) = & self . maybe_config_file {
2021-11-08 20:26:39 -05:00
if let Some ( config_imports ) = config_file . to_maybe_imports ( ) ? {
2021-10-10 17:26:22 -04:00
imports . extend ( config_imports ) ;
}
}
if self . flags . compat {
imports . extend ( compat ::get_node_imports ( ) ) ;
}
if imports . is_empty ( ) {
2021-11-08 20:26:39 -05:00
Ok ( None )
2021-10-10 17:26:22 -04:00
} else {
2021-11-08 20:26:39 -05:00
Ok ( Some ( imports ) )
2021-10-10 17:26:22 -04:00
}
}
/// This method is called when a module requested by the `JsRuntime` is not
/// available, or in other sub-commands that need to "load" a module graph.
/// The method will collect all the dependencies of the provided specifier,
/// optionally checks their integrity, optionally type checks them, and
/// ensures that any modules that needs to be transpiled is transpiled.
///
/// It then populates the `loadable_modules` with what can be loaded into v8.
pub ( crate ) async fn prepare_module_load (
2021-09-24 11:10:42 -04:00
& self ,
2021-10-10 17:26:22 -04:00
roots : Vec < ModuleSpecifier > ,
is_dynamic : bool ,
lib : emit ::TypeLib ,
2021-05-17 03:44:38 -04:00
root_permissions : Permissions ,
dynamic_permissions : Permissions ,
2021-04-28 14:17:04 -04:00
) -> Result < ( ) , AnyError > {
2021-10-10 17:26:22 -04:00
let mut cache = cache ::FetchCacher ::new (
self . dir . gen_cache . clone ( ) ,
self . file_fetcher . clone ( ) ,
root_permissions . clone ( ) ,
dynamic_permissions . clone ( ) ,
) ;
let maybe_locker = as_maybe_locker ( self . lockfile . clone ( ) ) ;
2021-11-08 20:26:39 -05:00
let maybe_imports = self . get_maybe_imports ( ) ? ;
2021-10-28 04:11:38 -04:00
let node_resolver = NodeEsmResolver ::new (
2021-11-08 20:26:39 -05:00
self . maybe_import_map . clone ( ) . map ( ImportMapResolver ::new ) ,
2021-10-28 04:11:38 -04:00
) ;
2021-11-08 20:26:39 -05:00
let maybe_import_map_resolver =
self . maybe_import_map . clone ( ) . map ( ImportMapResolver ::new ) ;
let maybe_jsx_resolver = self
. maybe_config_file
. as_ref ( )
. map ( | cf | {
cf . to_maybe_jsx_import_source_module ( )
. map ( | im | JsxResolver ::new ( im , maybe_import_map_resolver . clone ( ) ) )
} )
. flatten ( ) ;
2021-10-18 13:36:28 -04:00
let maybe_resolver = if self . flags . compat {
Some ( node_resolver . as_resolver ( ) )
2021-11-08 20:26:39 -05:00
} else if maybe_jsx_resolver . is_some ( ) {
// the JSX resolver offloads to the import map if present, otherwise uses
// the default Deno explicit import resolution.
maybe_jsx_resolver . as_ref ( ) . map ( | jr | jr . as_resolver ( ) )
2021-10-18 13:36:28 -04:00
} else {
2021-11-08 20:26:39 -05:00
maybe_import_map_resolver
. as_ref ( )
. map ( | im | im . as_resolver ( ) )
2021-10-18 13:36:28 -04:00
} ;
// TODO(bartlomieju): this is very make-shift, is there an existing API
// that we could include it like with "maybe_imports"?
let roots = if self . flags . compat {
let mut r = vec! [ compat ::GLOBAL_URL . clone ( ) ] ;
r . extend ( roots ) ;
r
} else {
roots
} ;
2021-10-10 17:26:22 -04:00
let graph = deno_graph ::create_graph (
roots ,
is_dynamic ,
maybe_imports ,
& mut cache ,
2021-10-18 13:36:28 -04:00
maybe_resolver ,
2021-10-10 17:26:22 -04:00
maybe_locker ,
None ,
)
. await ;
// If there was a locker, validate the integrity of all the modules in the
// locker.
emit ::lock ( & graph ) ;
// Determine any modules that have already been emitted this session and
// should be skipped.
let reload_exclusions : HashSet < ModuleSpecifier > = {
2021-10-19 10:01:46 -04:00
let graph_data = self . graph_data . lock ( ) ;
graph_data . modules . keys ( ) . cloned ( ) . collect ( )
2021-06-19 10:14:43 -04:00
} ;
2021-04-28 14:17:04 -04:00
2021-10-10 17:26:22 -04:00
let config_type = if self . flags . no_check {
emit ::ConfigType ::Emit
2021-04-28 14:17:04 -04:00
} else {
2021-10-10 17:26:22 -04:00
emit ::ConfigType ::Check {
tsc_emit : true ,
2021-04-28 14:17:04 -04:00
lib ,
}
} ;
2021-10-10 17:26:22 -04:00
let ( ts_config , maybe_ignored_options ) =
emit ::get_ts_config ( config_type , self . maybe_config_file . as_ref ( ) , None ) ? ;
let graph = Arc ::new ( graph ) ;
// we will store this in proc state later, as if we were to return it from
// prepare_load, some dynamic errors would not be catchable
let maybe_graph_error = graph . valid ( ) . err ( ) ;
if emit ::valid_emit (
graph . as_ref ( ) ,
& cache ,
& ts_config ,
self . flags . reload ,
& reload_exclusions ,
) {
if let Some ( root ) = graph . roots . get ( 0 ) {
log ::debug! ( " specifier \" {} \" and dependencies have valid emit, skipping checking and emitting " , root ) ;
} else {
log ::debug! ( " rootless graph, skipping checking and emitting " ) ;
}
} else {
if let Some ( ignored_options ) = maybe_ignored_options {
log ::warn! ( " {} " , ignored_options ) ;
}
let emit_result = if self . flags . no_check {
let options = emit ::EmitOptions {
ts_config ,
reload_exclusions ,
reload : self . flags . reload ,
} ;
emit ::emit ( graph . as_ref ( ) , & mut cache , options ) ?
} else {
// here, we are type checking, so we want to error here if any of the
// type only dependencies are missing or we have other errors with them
// where as if we are not type checking, we shouldn't care about these
// errors, and they don't get returned in `graph.valid()` above.
graph . valid_types_only ( ) ? ;
let maybe_config_specifier = self
. maybe_config_file
. as_ref ( )
. map ( | cf | ModuleSpecifier ::from_file_path ( & cf . path ) . unwrap ( ) ) ;
let options = emit ::CheckOptions {
debug : self . flags . log_level = = Some ( log ::Level ::Debug ) ,
2021-10-25 19:34:16 -04:00
emit_with_diagnostics : false ,
2021-10-10 17:26:22 -04:00
maybe_config_specifier ,
ts_config ,
} ;
for root in & graph . roots {
let root_str = root . to_string ( ) ;
// `$deno$` specifiers are internal specifiers, printing out that
// they are being checked is confusing to a user, since they don't
// actually exist, so we will simply indicate that a generated module
// is being checked instead of the cryptic internal module
if ! root_str . contains ( " $deno$ " ) {
log ::info! ( " {} {} " , colors ::green ( " Check " ) , root ) ;
} else {
log ::info! ( " {} a generated module " , colors ::green ( " Check " ) )
}
}
emit ::check_and_maybe_emit ( graph . clone ( ) , & mut cache , options ) ?
} ;
log ::debug! ( " {} " , emit_result . stats ) ;
// if the graph is not valid then the diagnostics returned are bogus and
// should just be ignored so that module loading can proceed to allow the
// "real" error to be surfaced
if ! emit_result . diagnostics . is_empty ( ) & & maybe_graph_error . is_none ( ) {
return Err ( anyhow! ( emit_result . diagnostics ) ) ;
}
}
{
2021-10-19 10:01:46 -04:00
let mut graph_data = self . graph_data . lock ( ) ;
// we iterate over the graph, looking for any modules that were emitted, or
// should be loaded as their un-emitted source and add them to the in memory
// cache of modules for loading by deno_core.
graph_data
. modules
. extend ( emit ::to_module_sources ( graph . as_ref ( ) , & cache ) ) ;
// since we can't store the graph in proc state, because proc state needs to
// be thread safe because of the need to provide source map resolution and
// the graph needs to not be thread safe (due to wasmbind_gen constraints),
// we have no choice but to extract out other meta data from the graph to
// provide the correct loading behaviors for CLI
graph_data . resolution_map . extend ( graph . resolution_map ( ) ) ;
graph_data . maybe_graph_error = maybe_graph_error ;
2021-10-10 17:26:22 -04:00
}
// any updates to the lockfile should be updated now
2021-04-28 14:17:04 -04:00
if let Some ( ref lockfile ) = self . lockfile {
2021-07-06 23:48:01 -04:00
let g = lockfile . lock ( ) ;
2021-04-28 14:17:04 -04:00
g . write ( ) ? ;
}
Ok ( ( ) )
}
2021-10-10 17:26:22 -04:00
pub ( crate ) fn resolve (
2021-09-24 11:10:42 -04:00
& self ,
2021-10-10 17:26:22 -04:00
specifier : & str ,
referrer : & str ,
) -> Result < ModuleSpecifier , AnyError > {
2021-10-19 10:01:46 -04:00
if let Ok ( s ) = deno_core ::resolve_url_or_path ( referrer ) {
let maybe_resolved = {
let graph_data = self . graph_data . lock ( ) ;
let resolved_specifier = graph_data
. resolution_map
. get ( & s )
. and_then ( | map | map . get ( specifier ) ) ;
resolved_specifier . cloned ( )
} ;
if let Some ( resolved ) = maybe_resolved {
2021-10-10 17:26:22 -04:00
match resolved {
Some ( Ok ( ( specifier , span ) ) ) = > {
2021-10-19 10:01:46 -04:00
let mut graph_data = self . graph_data . lock ( ) ;
graph_data . resolved_map . insert ( specifier . clone ( ) , span ) ;
return Ok ( specifier ) ;
2021-10-10 17:26:22 -04:00
}
Some ( Err ( err ) ) = > {
return Err ( custom_error (
" TypeError " ,
2021-10-21 10:18:18 -04:00
format! ( " {} \n " , err . to_string_with_range ( ) ) ,
2021-10-10 17:26:22 -04:00
) )
}
_ = > ( ) ,
}
2020-09-24 18:31:17 -04:00
}
2021-10-10 17:26:22 -04:00
}
2021-10-19 10:01:46 -04:00
2021-10-10 17:26:22 -04:00
// FIXME(bartlomieju): hacky way to provide compatibility with repl
let referrer = if referrer . is_empty ( ) & & self . flags . repl {
deno_core ::DUMMY_SPECIFIER
2020-09-24 18:31:17 -04:00
} else {
2021-10-10 17:26:22 -04:00
referrer
2020-10-22 20:50:15 -04:00
} ;
2021-10-10 17:26:22 -04:00
if let Some ( import_map ) = & self . maybe_import_map {
import_map
. resolve ( specifier , referrer )
. map_err ( | err | err . into ( ) )
} else {
deno_core ::resolve_import ( specifier , referrer ) . map_err ( | err | err . into ( ) )
2020-07-02 11:54:51 -04:00
}
2020-05-29 10:32:15 -04:00
}
2020-12-15 00:52:55 -05:00
pub fn load (
2020-05-29 10:32:15 -04:00
& self ,
2020-12-15 00:52:55 -05:00
specifier : ModuleSpecifier ,
2020-10-22 20:50:15 -04:00
maybe_referrer : Option < ModuleSpecifier > ,
2021-10-10 17:26:22 -04:00
is_dynamic : bool ,
2020-12-15 00:52:55 -05:00
) -> Result < ModuleSource , AnyError > {
2021-10-10 17:26:22 -04:00
log ::debug! (
" specifier: {} maybe_referrer: {} is_dynamic: {} " ,
specifier ,
maybe_referrer
. as_ref ( )
. map ( | s | s . to_string ( ) )
. unwrap_or_else ( | | " <none> " . to_string ( ) ) ,
is_dynamic
) ;
2021-10-19 10:01:46 -04:00
{
let graph_data = self . graph_data . lock ( ) ;
if let Some ( module_result ) = graph_data . modules . get ( & specifier ) {
if let Ok ( module_source ) = module_result {
return Ok ( module_source . clone ( ) ) ;
2021-10-10 17:26:22 -04:00
}
2021-10-19 10:01:46 -04:00
} else {
2021-10-10 17:26:22 -04:00
if maybe_referrer . is_some ( ) & & ! is_dynamic {
2021-10-19 10:01:46 -04:00
if let Some ( span ) = graph_data . resolved_map . get ( & specifier ) {
return Err ( custom_error (
" NotFound " ,
format! ( " Cannot load module \" {} \" . \n at {} " , specifier , span ) ,
) ) ;
2021-10-10 17:26:22 -04:00
}
2020-12-15 00:52:55 -05:00
}
2021-10-19 10:01:46 -04:00
return Err ( custom_error (
" NotFound " ,
format! ( " Cannot load module \" {} \" . " , specifier ) ,
) ) ;
}
}
// If we're this far it means that there was an error for this module load.
let mut graph_data = self . graph_data . lock ( ) ;
let err = graph_data
. modules
. get ( & specifier )
. unwrap ( )
. as_ref ( )
. unwrap_err ( ) ;
// this is the "pending" error we will return
let err = if let Some ( error_class ) = get_custom_error_class ( err ) {
if error_class = = " NotFound " & & maybe_referrer . is_some ( ) & & ! is_dynamic {
// in situations where we were to try to load a module that wasn't
// emitted and we can't run the original source code (it isn't)
// JavaScript, we will load a blank module instead. This is
// usually caused by people exporting type only exports and not
// type checking.
if let Some ( span ) = graph_data . resolved_map . get ( & specifier ) {
log ::warn! ( " {}: Cannot load module \" {} \" . \n at {} \n If the source module contains only types, use `import type` and `export type` to import it instead. " , colors ::yellow ( " warning " ) , specifier , span ) ;
return Ok ( ModuleSource {
code : " " . to_string ( ) ,
module_url_found : specifier . to_string ( ) ,
module_url_specified : specifier . to_string ( ) ,
} ) ;
}
}
custom_error ( error_class , err . to_string ( ) )
} else {
anyhow! ( err . to_string ( ) )
} ;
// if there is a pending graph error though we haven't returned, we
// will return that one
if let Some ( graph_error ) = graph_data . maybe_graph_error . take ( ) {
log ::debug! ( " returning cached graph error " ) ;
if let Some ( span ) = graph_data . resolved_map . get ( & specifier ) {
if ! span . specifier . as_str ( ) . contains ( " $deno " ) {
return Err ( custom_error (
get_module_graph_error_class ( & graph_error ) ,
format! ( " {} \n at {} " , graph_error , span ) ,
) ) ;
}
}
Err ( graph_error . into ( ) )
} else {
Err ( err )
}
2019-11-04 10:38:52 -05:00
}
2020-12-15 00:52:55 -05:00
// TODO(@kitsonk) this should be refactored to get it from the module graph
2020-10-22 20:50:15 -04:00
fn get_emit ( & self , url : & Url ) -> Option < ( Vec < u8 > , Option < Vec < u8 > > ) > {
match url . scheme ( ) {
// we should only be looking for emits for schemes that denote external
// modules, which the disk_cache supports
2021-04-07 09:22:14 -04:00
" wasm " | " file " | " http " | " https " | " data " | " blob " = > ( ) ,
2020-10-22 20:50:15 -04:00
_ = > {
return None ;
}
}
let emit_path = self
. dir
. gen_cache
2021-07-30 09:03:41 -04:00
. get_cache_filename_with_extension ( url , " js " ) ? ;
2020-10-22 20:50:15 -04:00
let emit_map_path = self
. dir
. gen_cache
2021-07-30 09:03:41 -04:00
. get_cache_filename_with_extension ( url , " js.map " ) ? ;
2020-10-22 20:50:15 -04:00
if let Ok ( code ) = self . dir . gen_cache . get ( & emit_path ) {
let maybe_map = if let Ok ( map ) = self . dir . gen_cache . get ( & emit_map_path ) {
Some ( map )
} else {
None
} ;
Some ( ( code , maybe_map ) )
} else {
None
}
}
2019-11-04 10:38:52 -05:00
}
2020-10-22 20:50:15 -04:00
// TODO(@kitsonk) this is only temporary, but should be refactored to somewhere
// else, like a refactored file_fetcher.
2021-09-24 11:10:42 -04:00
impl SourceMapGetter for ProcState {
2020-10-22 20:50:15 -04:00
fn get_source_map ( & self , file_name : & str ) -> Option < Vec < u8 > > {
2021-02-17 13:47:18 -05:00
if let Ok ( specifier ) = resolve_url ( file_name ) {
if let Some ( ( code , maybe_map ) ) = self . get_emit ( & specifier ) {
2021-02-07 18:14:05 -05:00
let code = String ::from_utf8 ( code ) . unwrap ( ) ;
source_map_from_code ( code ) . or ( maybe_map )
2021-10-10 17:26:22 -04:00
} else if let Ok ( source ) = self . load ( specifier , None , false ) {
2021-01-05 18:10:36 -05:00
source_map_from_code ( source . code )
2020-10-22 20:50:15 -04:00
} else {
None
}
2020-06-15 11:53:05 -04:00
} else {
2020-10-22 20:50:15 -04:00
None
2020-06-10 10:02:41 -04:00
}
2020-10-22 20:50:15 -04:00
}
2020-06-01 15:01:51 -04:00
2020-10-22 20:50:15 -04:00
fn get_source_line (
& self ,
file_name : & str ,
line_number : usize ,
) -> Option < String > {
2021-02-17 13:47:18 -05:00
if let Ok ( specifier ) = resolve_url ( file_name ) {
2020-11-15 21:19:31 -05:00
self . file_fetcher . get_source ( & specifier ) . map ( | out | {
2020-11-05 19:38:21 -05:00
// Do NOT use .lines(): it skips the terminating empty line.
// (due to internally using .split_terminator() instead of .split())
let lines : Vec < & str > = out . source . split ( '\n' ) . collect ( ) ;
2021-10-18 12:05:36 -04:00
if line_number > = lines . len ( ) {
format! (
" {} Couldn't format source line: Line {} is out of bounds (source may have changed at runtime) " ,
crate ::colors ::yellow ( " Warning " ) , line_number + 1 ,
)
} else {
lines [ line_number ] . to_string ( )
}
2020-11-05 19:38:21 -05:00
} )
2020-10-22 20:50:15 -04:00
} else {
None
}
}
2020-06-01 15:01:51 -04:00
}
2021-01-05 18:10:36 -05:00
fn source_map_from_code ( code : String ) -> Option < Vec < u8 > > {
let lines : Vec < & str > = code . split ( '\n' ) . collect ( ) ;
if let Some ( last_line ) = lines . last ( ) {
if last_line
. starts_with ( " //# sourceMappingURL=data:application/json;base64, " )
{
let input = last_line . trim_start_matches (
" //# sourceMappingURL=data:application/json;base64, " ,
) ;
let decoded_map = base64 ::decode ( input )
. expect ( " Unable to decode source map from emitted file. " ) ;
Some ( decoded_map )
} else {
None
}
} else {
None
}
}