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 ;
use crate ::config_file ::IgnoredCompilerOptions ;
use crate ::diagnostics ::Diagnostics ;
use crate ::emit ;
use crate ::errors ::get_error_class_name ;
2021-11-29 17:23:30 -05:00
use crate ::flags ;
2021-09-24 11:10:42 -04:00
use crate ::proc_state ::ProcState ;
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-11-01 21:51:56 -05:00
2021-11-16 09:02:28 -05:00
use deno_core ::anyhow ::Context ;
2021-10-10 17:26:22 -04:00
use deno_core ::error ::custom_error ;
2020-12-31 16:43:54 -05:00
use deno_core ::error ::generic_error ;
2020-09-14 12:48:57 -04:00
use deno_core ::error ::AnyError ;
2021-02-17 13:47:18 -05:00
use deno_core ::resolve_url_or_path ;
2021-10-10 17:26:22 -04:00
use deno_core ::serde_json ;
2020-09-21 12:36:37 -04:00
use deno_core ::serde_json ::Value ;
2021-10-10 17:26:22 -04:00
use deno_core ::ModuleSpecifier ;
2020-09-10 09:57:45 -04:00
use deno_core ::OpState ;
2021-10-10 17:26:22 -04:00
use deno_graph ;
2020-12-29 23:17:17 -05:00
use deno_runtime ::permissions ::Permissions ;
2021-09-10 21:38:24 -04:00
use import_map ::ImportMap ;
2020-09-16 12:43:08 -04:00
use serde ::Deserialize ;
2021-10-05 16:38:27 -04:00
use serde ::Serialize ;
2020-09-10 09:57:45 -04:00
use std ::cell ::RefCell ;
2020-01-21 11:50:06 -05:00
use std ::collections ::HashMap ;
2021-10-10 17:26:22 -04:00
use std ::collections ::HashSet ;
2020-08-18 12:30:13 -04:00
use std ::rc ::Rc ;
2020-12-29 23:17:17 -05:00
use std ::sync ::Arc ;
2020-01-21 11:50:06 -05:00
2020-09-10 09:57:45 -04:00
pub fn init ( rt : & mut deno_core ::JsRuntime ) {
2021-04-12 15:55:05 -04:00
super ::reg_async ( rt , " op_emit " , op_emit ) ;
2020-01-21 11:50:06 -05:00
}
2020-12-31 16:43:54 -05:00
#[ derive(Debug, Deserialize) ]
enum RuntimeBundleType {
2021-04-25 16:54:57 -04:00
#[ serde(rename = " module " ) ]
Module ,
#[ serde(rename = " classic " ) ]
Classic ,
2020-12-31 16:43:54 -05:00
}
2021-10-10 17:26:22 -04:00
impl < ' a > From < & ' a RuntimeBundleType > for emit ::BundleType {
fn from ( bundle_type : & ' a RuntimeBundleType ) -> Self {
match bundle_type {
RuntimeBundleType ::Classic = > Self ::Classic ,
RuntimeBundleType ::Module = > Self ::Module ,
}
}
}
2020-12-31 16:43:54 -05:00
#[ derive(Debug, Deserialize) ]
2020-01-21 11:50:06 -05:00
#[ serde(rename_all = " camelCase " ) ]
2020-12-31 16:43:54 -05:00
struct EmitArgs {
bundle : Option < RuntimeBundleType > ,
check : Option < bool > ,
compiler_options : Option < HashMap < String , Value > > ,
import_map : Option < Value > ,
import_map_path : Option < String > ,
root_specifier : String ,
2021-09-07 10:39:32 -04:00
sources : Option < HashMap < String , Arc < String > > > ,
2020-01-21 11:50:06 -05:00
}
2021-10-05 16:38:27 -04:00
#[ derive(Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
struct EmitResult {
2021-10-10 17:26:22 -04:00
diagnostics : Diagnostics ,
2021-10-05 16:38:27 -04:00
files : HashMap < String , String > ,
2021-10-10 17:26:22 -04:00
#[ serde(rename = " ignoredOptions " ) ]
maybe_ignored_options : Option < IgnoredCompilerOptions > ,
stats : emit ::Stats ,
}
2021-11-08 20:26:39 -05:00
/// Provides inferred imported modules from configuration options, like the
/// `"types"` and `"jsxImportSource"` imports.
2021-10-10 17:26:22 -04:00
fn to_maybe_imports (
referrer : & ModuleSpecifier ,
maybe_options : Option < & HashMap < String , Value > > ,
) -> Option < Vec < ( ModuleSpecifier , Vec < String > ) > > {
2021-11-08 20:26:39 -05:00
let options = maybe_options ? ;
let mut imports = Vec ::new ( ) ;
if let Some ( types_value ) = options . get ( " types " ) {
if let Ok ( types ) =
serde_json ::from_value ::< Vec < String > > ( types_value . clone ( ) )
{
imports . extend ( types ) ;
}
}
if let Some ( jsx_value ) = options . get ( " jsx " ) {
if let Ok ( jsx ) = serde_json ::from_value ::< String > ( jsx_value . clone ( ) ) {
let jsx_import_source =
if let Some ( jsx_import_source_value ) = options . get ( " jsxImportSource " ) {
if let Ok ( jsx_import_source ) =
serde_json ::from_value ::< String > ( jsx_import_source_value . clone ( ) )
{
jsx_import_source
} else {
" react " . to_string ( )
}
} else {
" react " . to_string ( )
} ;
match jsx . as_str ( ) {
" react-jsx " = > {
imports . push ( format! ( " {} /jsx-runtime " , jsx_import_source ) ) ;
}
" react-jsxdev " = > {
imports . push ( format! ( " {} /jsx-dev-runtime " , jsx_import_source ) ) ;
}
_ = > ( ) ,
}
}
}
if ! imports . is_empty ( ) {
Some ( vec! [ ( referrer . clone ( ) , imports ) ] )
} else {
None
}
}
/// Converts the compiler options to the JSX import source module that will be
/// loaded when transpiling JSX.
fn to_maybe_jsx_import_source_module (
maybe_options : Option < & HashMap < String , Value > > ,
) -> Option < String > {
let options = maybe_options ? ;
let jsx_value = options . get ( " jsx " ) ? ;
let jsx : String = serde_json ::from_value ( jsx_value . clone ( ) ) . ok ( ) ? ;
match jsx . as_str ( ) {
" react-jsx " = > Some ( " jsx-runtime " . to_string ( ) ) ,
" react-jsxdev " = > Some ( " jsx-dev-runtime " . to_string ( ) ) ,
_ = > None ,
}
2021-10-05 16:38:27 -04:00
}
2020-12-31 16:43:54 -05:00
async fn op_emit (
2020-09-10 09:57:45 -04:00
state : Rc < RefCell < OpState > > ,
2021-10-05 16:38:27 -04:00
args : EmitArgs ,
2021-05-08 08:37:42 -04:00
_ : ( ) ,
2021-10-05 16:38:27 -04:00
) -> Result < EmitResult , AnyError > {
2020-12-31 16:43:54 -05:00
deno_runtime ::ops ::check_unstable2 ( & state , " Deno.emit " ) ;
2021-01-28 20:33:58 -05:00
let root_specifier = args . root_specifier ;
2021-11-03 09:27:36 -04:00
let ps = {
let state = state . borrow ( ) ;
state . borrow ::< ProcState > ( ) . clone ( )
} ;
let mut runtime_permissions = {
let state = state . borrow ( ) ;
state . borrow ::< Permissions > ( ) . clone ( )
} ;
2021-10-10 17:26:22 -04:00
let mut cache : Box < dyn cache ::CacherLoader > =
if let Some ( sources ) = & args . sources {
Box ::new ( cache ::MemoryCacher ::new ( sources . clone ( ) ) )
2020-11-01 21:51:56 -05:00
} else {
2021-10-10 17:26:22 -04:00
Box ::new ( cache ::FetchCacher ::new (
ps . dir . gen_cache . clone ( ) ,
ps . file_fetcher . clone ( ) ,
2021-02-17 08:32:57 -05:00
runtime_permissions . clone ( ) ,
2021-05-17 03:44:38 -04:00
runtime_permissions . clone ( ) ,
2021-10-10 17:26:22 -04:00
) )
2020-11-01 21:51:56 -05:00
} ;
2021-11-08 20:26:39 -05:00
let maybe_import_map_resolver = if let Some ( import_map_str ) =
args . import_map_path
{
2021-02-17 13:47:18 -05:00
let import_map_specifier = resolve_url_or_path ( & import_map_str )
. context ( format! ( " Bad URL ( \" {} \" ) for import map. " , import_map_str ) ) ? ;
2020-12-31 16:43:54 -05:00
let import_map = if let Some ( value ) = args . import_map {
2021-02-17 13:47:18 -05:00
ImportMap ::from_json ( import_map_specifier . as_str ( ) , & value . to_string ( ) ) ?
2020-12-31 16:43:54 -05:00
} else {
2021-09-24 11:10:42 -04:00
let file = ps
2021-02-17 08:32:57 -05:00
. file_fetcher
2021-04-11 22:15:43 -04:00
. fetch ( & import_map_specifier , & mut runtime_permissions )
2021-05-04 08:27:20 -04:00
. await
. map_err ( | e | {
generic_error ( format! (
" Unable to load '{}' import map: {} " ,
import_map_specifier , e
) )
} ) ? ;
2021-02-17 08:32:57 -05:00
ImportMap ::from_json ( import_map_specifier . as_str ( ) , & file . source ) ?
2020-12-31 16:43:54 -05:00
} ;
2021-11-08 20:26:39 -05:00
Some ( ImportMapResolver ::new ( Arc ::new ( import_map ) ) )
2020-12-31 16:43:54 -05:00
} else if args . import_map . is_some ( ) {
return Err ( generic_error ( " An importMap was specified, but no importMapPath was provided, which is required. " ) ) ;
2020-08-28 11:08:24 -04:00
} else {
2020-12-31 16:43:54 -05:00
None
} ;
2021-11-08 20:26:39 -05:00
let maybe_jsx_resolver =
to_maybe_jsx_import_source_module ( args . compiler_options . as_ref ( ) )
. map ( | im | JsxResolver ::new ( im , maybe_import_map_resolver . clone ( ) ) ) ;
let maybe_resolver = if maybe_jsx_resolver . is_some ( ) {
maybe_jsx_resolver . as_ref ( ) . map ( | jr | jr . as_resolver ( ) )
} else {
maybe_import_map_resolver
. as_ref ( )
. map ( | imr | imr . as_resolver ( ) )
} ;
2021-10-10 17:26:22 -04:00
let roots = vec! [ resolve_url_or_path ( & root_specifier ) ? ] ;
let maybe_imports =
to_maybe_imports ( & roots [ 0 ] , args . compiler_options . as_ref ( ) ) ;
let graph = Arc ::new (
deno_graph ::create_graph (
roots ,
true ,
maybe_imports ,
cache . as_mut_loader ( ) ,
2021-11-08 20:26:39 -05:00
maybe_resolver ,
2021-10-10 17:26:22 -04:00
None ,
None ,
)
. await ,
) ;
// There are certain graph errors that we want to return as an error of an op,
// versus something that gets returned as a diagnostic of the op, this is
// handled here.
if let Err ( err ) = graph . valid ( ) {
let err : AnyError = err . into ( ) ;
if get_error_class_name ( & err ) = = " PermissionDenied " {
return Err ( err ) ;
}
}
let check = args . check . unwrap_or ( true ) ;
2021-09-24 11:10:42 -04:00
let debug = ps . flags . log_level = = Some ( log ::Level ::Debug ) ;
2021-10-10 17:26:22 -04:00
let tsc_emit = check & & args . bundle . is_none ( ) ;
let ( ts_config , maybe_ignored_options ) = emit ::get_ts_config (
emit ::ConfigType ::RuntimeEmit { tsc_emit } ,
None ,
args . compiler_options . as_ref ( ) ,
) ? ;
let ( files , mut diagnostics , stats ) = if check & & args . bundle . is_none ( ) {
let ( diagnostics , stats ) = if args . sources . is_none ( )
& & emit ::valid_emit (
graph . as_ref ( ) ,
cache . as_cacher ( ) ,
& ts_config ,
ps . flags . reload ,
& HashSet ::default ( ) ,
) {
log ::debug! (
" cache is valid for \" {} \" , skipping check/emit " ,
root_specifier
) ;
( Diagnostics ::default ( ) , emit ::Stats ::default ( ) )
} else {
let emit_result = emit ::check_and_maybe_emit (
graph . clone ( ) ,
cache . as_mut_cacher ( ) ,
emit ::CheckOptions {
2021-11-29 17:23:30 -05:00
check : flags ::CheckFlag ::All ,
2021-10-10 17:26:22 -04:00
debug ,
emit_with_diagnostics : true ,
maybe_config_specifier : None ,
ts_config ,
2021-11-23 16:20:30 -05:00
reload : true ,
2021-10-10 17:26:22 -04:00
} ,
) ? ;
( emit_result . diagnostics , emit_result . stats )
} ;
let files = emit ::to_file_map ( graph . as_ref ( ) , cache . as_mut_cacher ( ) ) ;
( files , diagnostics , stats )
} else if let Some ( bundle ) = & args . bundle {
let ( diagnostics , stats ) = if check {
if ts_config . get_declaration ( ) {
return Err ( custom_error ( " TypeError " , " The bundle option is set, but the compiler option of `declaration` is true which is not currently supported. " ) ) ;
}
let emit_result = emit ::check_and_maybe_emit (
graph . clone ( ) ,
cache . as_mut_cacher ( ) ,
emit ::CheckOptions {
2021-11-29 17:23:30 -05:00
check : flags ::CheckFlag ::All ,
2021-10-10 17:26:22 -04:00
debug ,
emit_with_diagnostics : true ,
maybe_config_specifier : None ,
ts_config : ts_config . clone ( ) ,
2021-11-23 16:20:30 -05:00
reload : true ,
2021-10-10 17:26:22 -04:00
} ,
) ? ;
( emit_result . diagnostics , emit_result . stats )
} else {
( Diagnostics ::default ( ) , Default ::default ( ) )
} ;
let ( emit , maybe_map ) = emit ::bundle (
graph . as_ref ( ) ,
emit ::BundleOptions {
bundle_type : bundle . into ( ) ,
ts_config ,
} ,
) ? ;
let mut files = HashMap ::new ( ) ;
files . insert ( " deno:///bundle.js " . to_string ( ) , emit ) ;
if let Some ( map ) = maybe_map {
files . insert ( " deno:///bundle.js.map " . to_string ( ) , map ) ;
}
( files , diagnostics , stats )
} else {
if ts_config . get_declaration ( ) {
return Err ( custom_error ( " TypeError " , " The option of `check` is false, but the compiler option of `declaration` is true which is not currently supported. " ) ) ;
}
let emit_result = emit ::emit (
graph . as_ref ( ) ,
cache . as_mut_cacher ( ) ,
emit ::EmitOptions {
reload : ps . flags . reload ,
ts_config ,
reload_exclusions : HashSet ::default ( ) ,
} ,
) ? ;
let files = emit ::to_file_map ( graph . as_ref ( ) , cache . as_mut_cacher ( ) ) ;
( files , emit_result . diagnostics , emit_result . stats )
} ;
// we want to add any errors that were returned as an `Err` earlier by adding
// them to the diagnostics.
diagnostics . extend_graph_errors ( graph . errors ( ) ) ;
2020-11-01 21:51:56 -05:00
2021-10-05 16:38:27 -04:00
Ok ( EmitResult {
2021-10-10 17:26:22 -04:00
diagnostics ,
2021-10-05 16:38:27 -04:00
files ,
2021-10-10 17:26:22 -04:00
maybe_ignored_options ,
stats ,
2021-10-05 16:38:27 -04:00
} )
2020-01-21 11:50:06 -05:00
}