summaryrefslogtreecommitdiff
path: root/cli/ops/runtime_compiler.rs
blob: 2dfd995b5730f5cf9a3c97e76ce6a73d80cfc70b (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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// 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_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 = "module")]
  Module,
  #[serde(rename = "classic")]
  Classic,
}

#[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,
  _: (),
) -> Result<Value, AnyError> {
  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::<Arc<ProgramState>>().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 mut is_dynamic = false;
  let handler: Arc<Mutex<dyn SpecifierHandler>> =
    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
        .map_err(|e| {
          generic_error(format!(
            "Unable to load '{}' import map: {}",
            import_map_specifier, e
          ))
        })?;
      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::Module) => BundleType::Module,
    Some(RuntimeBundleType::Classic) => BundleType::Classic,
    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,
  }))
}