diff options
Diffstat (limited to 'cli/ops/runtime_compiler.rs')
-rw-r--r-- | cli/ops/runtime_compiler.rs | 213 |
1 files changed, 160 insertions, 53 deletions
diff --git a/cli/ops/runtime_compiler.rs b/cli/ops/runtime_compiler.rs index d63d97f58..ab6992d19 100644 --- a/cli/ops/runtime_compiler.rs +++ b/cli/ops/runtime_compiler.rs @@ -1,27 +1,30 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -use crate::module_graph::BundleType; -use crate::module_graph::EmitOptions; -use crate::module_graph::GraphBuilder; +use crate::cache; +use crate::config_file::IgnoredCompilerOptions; +use crate::diagnostics::Diagnostics; +use crate::emit; +use crate::errors::get_error_class_name; use crate::proc_state::ProcState; -use crate::specifier_handler::FetchHandler; -use crate::specifier_handler::MemoryHandler; -use crate::specifier_handler::SpecifierHandler; +use crate::resolver::ImportMapResolver; +use deno_core::error::custom_error; use deno_core::error::generic_error; -use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::error::Context; -use deno_core::parking_lot::Mutex; use deno_core::resolve_url_or_path; +use deno_core::serde_json; use deno_core::serde_json::Value; +use deno_core::ModuleSpecifier; use deno_core::OpState; +use deno_graph; use deno_runtime::permissions::Permissions; use import_map::ImportMap; use serde::Deserialize; use serde::Serialize; use std::cell::RefCell; use std::collections::HashMap; +use std::collections::HashSet; use std::rc::Rc; use std::sync::Arc; @@ -37,6 +40,15 @@ enum RuntimeBundleType { Classic, } +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, + } + } +} + #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct EmitArgs { @@ -52,10 +64,21 @@ struct EmitArgs { #[derive(Serialize)] #[serde(rename_all = "camelCase")] struct EmitResult { - diagnostics: crate::diagnostics::Diagnostics, + diagnostics: Diagnostics, files: HashMap<String, String>, - ignored_options: Option<crate::config_file::IgnoredCompilerOptions>, - stats: crate::module_graph::Stats, + #[serde(rename = "ignoredOptions")] + maybe_ignored_options: Option<IgnoredCompilerOptions>, + stats: emit::Stats, +} + +fn to_maybe_imports( + referrer: &ModuleSpecifier, + maybe_options: Option<&HashMap<String, Value>>, +) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> { + let options = maybe_options.as_ref()?; + let types_value = options.get("types")?; + let types: Vec<String> = serde_json::from_value(types_value.clone()).ok()?; + Some(vec![(referrer.clone(), types)]) } async fn op_emit( @@ -65,23 +88,19 @@ async fn op_emit( ) -> Result<EmitResult, AnyError> { deno_runtime::ops::check_unstable2(&state, "Deno.emit"); let root_specifier = args.root_specifier; - let ps = state.borrow().borrow::<ProcState>().clone(); - let mut runtime_permissions = { - let state = state.borrow(); - state.borrow::<Permissions>().clone() - }; - // when we are actually resolving modules without provided sources, we should - // treat the root module as a dynamic import so that runtime permissions are - // applied. - let handler: Arc<Mutex<dyn SpecifierHandler>> = - if let Some(sources) = args.sources { - Arc::new(Mutex::new(MemoryHandler::new(sources))) + let state = state.borrow(); + let ps = state.borrow::<ProcState>(); + let mut runtime_permissions = { state.borrow::<Permissions>().clone() }; + let mut cache: Box<dyn cache::CacherLoader> = + if let Some(sources) = &args.sources { + Box::new(cache::MemoryCacher::new(sources.clone())) } else { - Arc::new(Mutex::new(FetchHandler::new( - &ps, + Box::new(cache::FetchCacher::new( + ps.dir.gen_cache.clone(), + ps.file_fetcher.clone(), runtime_permissions.clone(), runtime_permissions.clone(), - )?)) + )) }; let maybe_import_map = if let Some(import_map_str) = args.import_map_path { let import_map_specifier = resolve_url_or_path(&import_map_str) @@ -107,37 +126,125 @@ async fn op_emit( } else { None }; - let mut builder = GraphBuilder::new(handler, maybe_import_map, None); - let root_specifier = resolve_url_or_path(&root_specifier)?; - builder.add(&root_specifier, false).await.map_err(|_| { - type_error(format!( - "Unable to handle the given specifier: {}", - &root_specifier - )) - })?; - builder - .analyze_compiler_options(&args.compiler_options) - .await?; - let bundle_type = match args.bundle { - Some(RuntimeBundleType::Module) => BundleType::Module, - Some(RuntimeBundleType::Classic) => BundleType::Classic, - None => BundleType::None, - }; - let graph = builder.get_graph(); + let roots = vec![resolve_url_or_path(&root_specifier)?]; + let maybe_imports = + to_maybe_imports(&roots[0], args.compiler_options.as_ref()); + let maybe_resolver = maybe_import_map.as_ref().map(ImportMapResolver::new); + let graph = Arc::new( + deno_graph::create_graph( + roots, + true, + maybe_imports, + cache.as_mut_loader(), + maybe_resolver.as_ref().map(|r| r.as_resolver()), + 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); let debug = ps.flags.log_level == Some(log::Level::Debug); - let graph_errors = graph.get_errors(); - let (files, mut result_info) = graph.emit(EmitOptions { - bundle_type, - check: args.check.unwrap_or(true), - debug, - maybe_user_config: args.compiler_options, - })?; - result_info.diagnostics.extend_graph_errors(graph_errors); + 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 { + debug, + emit_with_diagnostics: true, + maybe_config_specifier: None, + ts_config, + }, + )?; + (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 { + debug, + emit_with_diagnostics: true, + maybe_config_specifier: None, + ts_config: ts_config.clone(), + }, + )?; + (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()); Ok(EmitResult { - diagnostics: result_info.diagnostics, + diagnostics, files, - ignored_options: result_info.maybe_ignored_options, - stats: result_info.stats, + maybe_ignored_options, + stats, }) } |