2020-01-02 15:13:47 -05:00
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
2020-09-05 20:34:02 -04:00
2019-11-04 10:38:52 -05:00
use crate ::deno_dir ;
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 ;
2020-05-29 10:32:15 -04:00
use crate ::import_map ::ImportMap ;
2020-09-25 04:24:51 -04:00
use crate ::inspector ::InspectorServer ;
2019-11-04 10:38:52 -05:00
use crate ::lockfile ::Lockfile ;
2020-09-15 10:33:14 -04:00
use crate ::media_type ::MediaType ;
2020-11-02 14:41:20 -05:00
use crate ::module_graph ::CheckOptions ;
use crate ::module_graph ::GraphBuilder ;
use crate ::module_graph ::TranspileOptions ;
use crate ::module_graph ::TypeLib ;
2020-05-04 14:10:59 -04:00
use crate ::permissions ::Permissions ;
2020-10-22 20:50:15 -04:00
use crate ::source_maps ::SourceMapGetter ;
2020-09-24 18:31:17 -04:00
use crate ::specifier_handler ::FetchHandler ;
2020-10-22 20:50:15 -04:00
use deno_core ::error ::generic_error ;
2020-09-14 12:48:57 -04:00
use deno_core ::error ::AnyError ;
2020-10-22 20:50:15 -04:00
use deno_core ::url ::Url ;
2020-01-05 11:56:18 -05:00
use deno_core ::ModuleSpecifier ;
2020-09-24 18:31:17 -04:00
use std ::cell ::RefCell ;
2019-11-04 10:38:52 -05:00
use std ::env ;
2020-09-24 18:31:17 -04:00
use std ::rc ::Rc ;
2019-11-04 10:38:52 -05:00
use std ::sync ::Arc ;
use std ::sync ::Mutex ;
2020-09-19 19:17:35 -04:00
pub fn exit_unstable ( api_name : & str ) {
eprintln! (
" Unstable API '{}'. The --unstable flag must be provided. " ,
api_name
) ;
std ::process ::exit ( 70 ) ;
}
2020-11-01 21:51:56 -05:00
// TODO(@kitsonk) probably can refactor this better with the graph.
pub struct CompiledModule {
pub code : String ,
pub name : String ,
}
2019-11-04 10:38:52 -05:00
/// This structure represents state of single "deno" program.
///
/// It is shared by all created workers (thus V8 isolates).
2020-10-13 07:35:35 -04:00
pub struct ProgramState {
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-11-05 19:38:21 -05:00
pub file_fetcher : FileFetcher ,
2020-10-23 17:01:54 -04:00
pub lockfile : Option < Arc < Mutex < Lockfile > > > ,
2020-05-29 10:32:15 -04:00
pub maybe_import_map : Option < ImportMap > ,
2020-09-25 04:24:51 -04:00
pub maybe_inspector_server : Option < Arc < InspectorServer > > ,
2019-11-04 10:38:52 -05:00
}
2020-10-13 07:35:35 -04:00
impl ProgramState {
2020-09-14 12:48:57 -04:00
pub fn new ( flags : flags ::Flags ) -> Result < Arc < Self > , AnyError > {
2019-11-04 10:38:52 -05:00
let custom_root = env ::var ( " DENO_DIR " ) . map ( String ::into ) . ok ( ) ;
let dir = deno_dir ::DenoDir ::new ( 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 ) ;
2020-08-18 12:30:13 -04:00
let ca_file = flags . ca_file . clone ( ) . or_else ( | | env ::var ( " DENO_CERT " ) . ok ( ) ) ;
2019-11-04 10:38:52 -05:00
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
} ;
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 ,
2020-08-18 12:30:13 -04:00
ca_file . as_deref ( ) ,
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
} ;
2020-05-29 10:32:15 -04:00
let maybe_import_map : Option < ImportMap > =
match flags . import_map_path . as_ref ( ) {
None = > None ,
Some ( file_path ) = > {
if ! flags . unstable {
2020-10-20 08:30:59 -04:00
exit_unstable ( " --import-map " )
2020-05-29 10:32:15 -04:00
}
Some ( ImportMap ::load ( file_path ) ? )
}
} ;
2020-09-25 04:24:51 -04:00
let maybe_inspect_host = flags . inspect . or ( flags . inspect_brk ) ;
let maybe_inspector_server = match maybe_inspect_host {
Some ( host ) = > Some ( Arc ::new ( InspectorServer ::new ( host ) ) ) ,
None = > None ,
} ;
2020-10-13 07:35:35 -04:00
let program_state = ProgramState {
2019-11-04 10:38:52 -05:00
dir ,
flags ,
file_fetcher ,
lockfile ,
2020-05-29 10:32:15 -04:00
maybe_import_map ,
2020-09-25 04:24:51 -04:00
maybe_inspector_server ,
2019-11-04 10:38:52 -05:00
} ;
2020-10-13 07:35:35 -04:00
Ok ( Arc ::new ( program_state ) )
2019-11-04 10:38:52 -05:00
}
2020-05-29 10:32:15 -04:00
/// This function is called when new module load is
2020-09-06 15:44:29 -04:00
/// initialized by the JsRuntime. Its resposibility is to collect
2020-05-29 10:32:15 -04:00
/// all dependencies and if it is required then also perform TS typecheck
/// and traspilation.
pub async fn prepare_module_load (
2020-08-18 12:30:13 -04:00
self : & Arc < Self > ,
2020-10-22 20:50:15 -04:00
specifier : ModuleSpecifier ,
2020-11-01 21:51:56 -05:00
lib : TypeLib ,
2020-10-26 15:56:00 -04:00
runtime_permissions : Permissions ,
2020-10-22 20:50:15 -04:00
is_dynamic : bool ,
2020-05-29 10:32:15 -04:00
maybe_import_map : Option < ImportMap > ,
2020-09-14 12:48:57 -04:00
) -> Result < ( ) , AnyError > {
2020-10-22 20:50:15 -04:00
let specifier = specifier . clone ( ) ;
2020-10-26 15:56:00 -04:00
// Workers are subject to the current runtime permissions. We do the
// permission check here early to avoid "wasting" time building a module
// graph for a module that cannot be loaded.
2020-11-01 21:51:56 -05:00
if lib = = TypeLib ::DenoWorker | | lib = = TypeLib ::UnstableDenoWorker {
2020-10-26 15:56:00 -04:00
runtime_permissions . check_specifier ( & specifier ) ? ;
}
2020-10-22 20:50:15 -04:00
let handler =
2020-10-26 15:56:00 -04:00
Rc ::new ( RefCell ::new ( FetchHandler ::new ( self , runtime_permissions ) ? ) ) ;
2020-10-23 17:01:54 -04:00
let mut builder =
2020-11-02 14:41:20 -05:00
GraphBuilder ::new ( handler , maybe_import_map , self . lockfile . clone ( ) ) ;
2020-10-22 20:50:15 -04:00
builder . add ( & specifier , is_dynamic ) . await ? ;
2020-10-23 17:01:54 -04:00
let mut graph = builder . get_graph ( ) ;
2020-10-22 20:50:15 -04:00
let debug = self . flags . log_level = = Some ( log ::Level ::Debug ) ;
let maybe_config_path = self . flags . config_path . clone ( ) ;
2020-05-29 10:32:15 -04:00
2020-09-24 18:31:17 -04:00
if self . flags . no_check {
let ( stats , maybe_ignored_options ) =
graph . transpile ( TranspileOptions {
2020-10-22 20:50:15 -04:00
debug ,
maybe_config_path ,
reload : self . flags . reload ,
2020-09-24 18:31:17 -04:00
} ) ? ;
2020-10-22 20:50:15 -04:00
debug! ( " {} " , stats ) ;
2020-09-24 18:31:17 -04:00
if let Some ( ignored_options ) = maybe_ignored_options {
2020-09-29 03:16:12 -04:00
eprintln! ( " {} " , ignored_options ) ;
2020-09-24 18:31:17 -04:00
}
} else {
2020-11-01 21:51:56 -05:00
let result_info = graph . check ( CheckOptions {
debug ,
emit : true ,
lib ,
maybe_config_path ,
reload : self . flags . reload ,
} ) ? ;
debug! ( " {} " , result_info . stats ) ;
if let Some ( ignored_options ) = result_info . maybe_ignored_options {
2020-10-22 20:50:15 -04:00
eprintln! ( " {} " , ignored_options ) ;
2020-07-08 05:26:39 -04:00
}
2020-11-01 21:51:56 -05:00
if ! result_info . diagnostics . is_empty ( ) {
return Err ( generic_error ( result_info . diagnostics . to_string ( ) ) ) ;
2020-10-22 20:50:15 -04:00
}
} ;
2020-05-29 10:32:15 -04:00
2020-07-02 11:54:51 -04:00
if let Some ( ref lockfile ) = self . lockfile {
let g = lockfile . lock ( ) . unwrap ( ) ;
g . write ( ) ? ;
}
2020-05-29 10:32:15 -04:00
Ok ( ( ) )
}
2020-10-22 20:50:15 -04:00
pub fn fetch_compiled_module (
2020-05-29 10:32:15 -04:00
& self ,
module_specifier : ModuleSpecifier ,
2020-10-22 20:50:15 -04:00
maybe_referrer : Option < ModuleSpecifier > ,
2020-09-14 12:48:57 -04:00
) -> Result < CompiledModule , AnyError > {
2020-11-05 19:38:21 -05:00
// TODO(@kitsonk) this really needs to be avoided and refactored out, as we
// really should just be getting this from the module graph.
2020-02-03 18:08:44 -05:00
let out = self
2019-11-04 10:38:52 -05:00
. file_fetcher
2020-11-15 21:19:31 -05:00
. get_source ( & module_specifier )
2020-05-29 10:32:15 -04:00
. expect ( " Cached source file doesn't exist " ) ;
2020-02-06 21:24:51 -05:00
2020-11-05 19:38:21 -05:00
let specifier = out . specifier . clone ( ) ;
let compiled_module = if let Some ( ( code , _ ) ) =
self . get_emit ( & specifier . as_url ( ) )
{
2020-10-22 20:50:15 -04:00
CompiledModule {
code : String ::from_utf8 ( code ) . unwrap ( ) ,
2020-11-05 19:38:21 -05:00
name : specifier . as_url ( ) . to_string ( ) ,
2020-10-22 20:50:15 -04:00
}
// We expect a compiled source for any non-JavaScript files, except for
// local files that have an unknown media type and no referrer (root modules
// that do not have an extension.)
} else if out . media_type ! = MediaType ::JavaScript
& & ! ( out . media_type = = MediaType ::Unknown
& & maybe_referrer . is_none ( )
2020-11-05 19:38:21 -05:00
& & specifier . as_url ( ) . scheme ( ) = = " file " )
2020-10-22 20:50:15 -04:00
{
let message = if let Some ( referrer ) = maybe_referrer {
format! ( " Compiled module not found \" {} \" \n From: {} \n If the source module contains only types, use `import type` and `export type` to import it instead. " , module_specifier , referrer )
} else {
format! ( " Compiled module not found \" {} \" \n If the source module contains only types, use `import type` and `export type` to import it instead. " , module_specifier )
} ;
info! ( " {}: {} " , crate ::colors ::yellow ( " warning " ) , message ) ;
CompiledModule {
code : " " . to_string ( ) ,
2020-11-05 19:38:21 -05:00
name : specifier . as_url ( ) . to_string ( ) ,
2020-07-17 09:50:17 -04:00
}
2020-05-29 10:32:15 -04:00
} else {
CompiledModule {
2020-11-05 19:38:21 -05:00
code : out . source ,
name : specifier . as_url ( ) . to_string ( ) ,
2020-05-29 10:32:15 -04:00
}
} ;
2020-02-03 18:08:44 -05:00
Ok ( compiled_module )
2019-11-04 10:38:52 -05:00
}
2020-10-22 20:50:15 -04:00
// TODO(@kitsonk) this should be a straight forward API on file_fetcher or
// whatever future refactors do...
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
" wasm " | " file " | " http " | " https " = > ( ) ,
_ = > {
return None ;
}
}
let emit_path = self
. dir
. gen_cache
. get_cache_filename_with_extension ( & url , " js " ) ;
let emit_map_path = self
. dir
. gen_cache
. get_cache_filename_with_extension ( & url , " js.map " ) ;
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
}
}
2020-09-19 19:17:35 -04:00
/// Quits the process if the --unstable flag was not provided.
///
/// This is intentionally a non-recoverable check so that people cannot probe
/// for unstable APIs from stable programs.
pub fn check_unstable ( & self , api_name : & str ) {
if ! self . flags . unstable {
exit_unstable ( api_name ) ;
}
}
2019-11-04 10:38:52 -05:00
#[ cfg(test) ]
2020-06-30 07:10:51 -04:00
pub fn mock (
argv : Vec < String > ,
maybe_flags : Option < flags ::Flags > ,
2020-10-13 07:35:35 -04:00
) -> Arc < ProgramState > {
ProgramState ::new ( flags ::Flags {
2020-08-18 12:30:13 -04:00
argv ,
.. maybe_flags . unwrap_or_default ( )
} )
. unwrap ( )
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.
impl SourceMapGetter for ProgramState {
fn get_source_map ( & self , file_name : & str ) -> Option < Vec < u8 > > {
if let Ok ( specifier ) = ModuleSpecifier ::resolve_url ( file_name ) {
if let Some ( ( code , maybe_map ) ) = self . get_emit ( & specifier . as_url ( ) ) {
if maybe_map . is_some ( ) {
maybe_map
} else {
let code = String ::from_utf8 ( code ) . unwrap ( ) ;
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
}
}
} 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 > {
if let Ok ( specifier ) = ModuleSpecifier ::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 ( ) ;
assert! ( lines . len ( ) > line_number ) ;
lines [ line_number ] . to_string ( )
} )
2020-10-22 20:50:15 -04:00
} else {
None
}
}
2020-06-01 15:01:51 -04:00
}
2019-11-04 10:38:52 -05:00
#[ test ]
fn thread_safe ( ) {
fn f < S : Send + Sync > ( _ : S ) { }
2020-10-13 07:35:35 -04:00
f ( ProgramState ::mock ( vec! [ ] , None ) ) ;
2019-11-04 10:38:52 -05:00
}