// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. use crate::import_map::ImportMap; use crate::module_graph::BundleType; use crate::module_graph::EmitOptions; use crate::module_graph::GraphBuilder; use crate::program_state::ProgramState; use crate::specifier_handler::FetchHandler; use crate::specifier_handler::MemoryHandler; use crate::specifier_handler::SpecifierHandler; 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::resolve_url_or_path; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::OpState; use deno_core::ZeroCopyBuf; use deno_runtime::permissions::Permissions; use serde::Deserialize; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use std::sync::Arc; use std::sync::Mutex; pub fn init(rt: &mut deno_core::JsRuntime) { super::reg_async(rt, "op_emit", op_emit); } #[derive(Debug, Deserialize)] enum RuntimeBundleType { #[serde(rename = "esm")] Esm, #[serde(rename = "iife")] Iife, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct EmitArgs { bundle: Option, check: Option, compiler_options: Option>, import_map: Option, import_map_path: Option, root_specifier: String, sources: Option>, } async fn op_emit( state: Rc>, args: Value, _data: Option, ) -> Result { deno_runtime::ops::check_unstable2(&state, "Deno.emit"); let args: EmitArgs = serde_json::from_value(args)?; let root_specifier = args.root_specifier; let program_state = state.borrow().borrow::>().clone(); let mut runtime_permissions = { let state = state.borrow(); state.borrow::().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 mut is_dynamic = false; let handler: Arc> = if let Some(sources) = args.sources { Arc::new(Mutex::new(MemoryHandler::new(sources))) } else { is_dynamic = true; Arc::new(Mutex::new(FetchHandler::new( &program_state, 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) .context(format!("Bad URL (\"{}\") for import map.", import_map_str))?; let import_map = if let Some(value) = args.import_map { ImportMap::from_json(import_map_specifier.as_str(), &value.to_string())? } else { let file = program_state .file_fetcher .fetch(&import_map_specifier, &mut runtime_permissions) .await?; ImportMap::from_json(import_map_specifier.as_str(), &file.source)? }; Some(import_map) } else if args.import_map.is_some() { return Err(generic_error("An importMap was specified, but no importMapPath was provided, which is required.")); } 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, is_dynamic) .await .map_err(|_| { type_error(format!( "Unable to handle the given specifier: {}", &root_specifier )) })?; let bundle_type = match args.bundle { Some(RuntimeBundleType::Esm) => BundleType::Esm, Some(RuntimeBundleType::Iife) => BundleType::Iife, None => BundleType::None, }; let graph = builder.get_graph(); let debug = program_state.flags.log_level == Some(log::Level::Debug); let (files, result_info) = graph.emit(EmitOptions { bundle_type, check: args.check.unwrap_or(true), debug, maybe_user_config: args.compiler_options, })?; Ok(json!({ "diagnostics": result_info.diagnostics, "files": files, "ignoredOptions": result_info.maybe_ignored_options, "stats": result_info.stats, })) }