summaryrefslogtreecommitdiff
path: root/cli/ops/runtime_compiler.rs
blob: 774b280bae49510296d492799a56f79c846d0a22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright 2018-2020 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::AnyError;
use deno_core::error::Context;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use deno_core::BufVec;
use deno_core::ModuleSpecifier;
use deno_core::OpState;
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_json_async(rt, "op_emit", op_emit);
}

#[derive(Debug, Deserialize)]
enum RuntimeBundleType {
  #[serde(rename = "esm")]
  Esm,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
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,
  sources: Option<HashMap<String, String>>,
}

async fn op_emit(
  state: Rc<RefCell<OpState>>,
  args: Value,
  _data: BufVec,
) -> Result<Value, AnyError> {
  deno_runtime::ops::check_unstable2(&state, "Deno.emit");
  let args: EmitArgs = serde_json::from_value(args)?;
  let program_state = state.borrow().borrow::<Arc<ProgramState>>().clone();
  let 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 mut is_dynamic = false;
  let handler: Arc<Mutex<dyn SpecifierHandler>> =
    if let Some(sources) = args.sources {
      is_dynamic = true;
      Arc::new(Mutex::new(MemoryHandler::new(sources)))
    } else {
      Arc::new(Mutex::new(FetchHandler::new(
        &program_state,
        runtime_permissions,
      )?))
    };
  let maybe_import_map = if let Some(import_map_str) = args.import_map_path {
    let import_map_specifier =
      ModuleSpecifier::resolve_url_or_path(&import_map_str).context(
        format!("Bad file path (\"{}\") for import map.", import_map_str),
      )?;
    let import_map_url = import_map_specifier.as_url();
    let import_map = if let Some(value) = args.import_map {
      ImportMap::from_json(&import_map_url.to_string(), &value.to_string())?
    } else {
      ImportMap::load(&import_map_str)?
    };
    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 =
    ModuleSpecifier::resolve_url_or_path(&args.root_specifier)?;
  builder.add(&root_specifier, is_dynamic).await?;
  let bundle_type = match args.bundle {
    Some(RuntimeBundleType::Esm) => BundleType::Esm,
    _ => 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,
  }))
}