Skip to main content

deno_core/modules/
mod.rs

1// Copyright 2018-2026 the Deno authors. MIT license.
2
3use std::borrow::Cow;
4use std::collections::HashMap;
5use std::future::Future;
6use std::sync::Arc;
7
8use serde::Deserialize;
9use serde::Serialize;
10use url::Url;
11
12use crate::FastStaticString;
13use crate::error::CoreError;
14use crate::error::CoreErrorKind;
15use crate::error::exception_to_err;
16use crate::fast_string::FastString;
17use crate::module_specifier::ModuleSpecifier;
18
19mod import_graph;
20mod loaders;
21mod map;
22mod module_map_data;
23pub(crate) mod recursive_load;
24
25#[cfg(all(test, not(miri)))]
26mod tests;
27
28pub use loaders::ExtCodeCache;
29pub(crate) use loaders::ExtModuleLoader;
30pub use loaders::FsModuleLoader;
31pub(crate) use loaders::LazyEsmModuleLoader;
32pub use loaders::ModuleLoadOptions;
33pub use loaders::ModuleLoadReferrer;
34pub use loaders::ModuleLoadResponse;
35pub use loaders::ModuleLoader;
36pub use loaders::ModuleLoaderError;
37pub use loaders::ModuleResolveResponse;
38pub use loaders::NoopModuleLoader;
39pub use loaders::StaticModuleLoader;
40pub(crate) use map::ModuleMap;
41pub(crate) use map::script_origin;
42pub(crate) use map::synthetic_module_evaluation_steps;
43pub(crate) use module_map_data::ModuleMapSnapshotData;
44pub(crate) use recursive_load::SideModuleKind;
45
46pub type ModuleId = usize;
47pub(crate) type ModuleLoadId = i32;
48
49/// The actual source code returned from the loader. Most embedders should
50/// try to return bytes and let deno_core interpret if the module should be
51/// converted to a string or not.
52#[derive(Debug, Hash, PartialEq, Eq)]
53pub enum ModuleSourceCode {
54  String(ModuleCodeString),
55  Bytes(ModuleCodeBytes),
56}
57
58impl ModuleSourceCode {
59  pub fn as_bytes(&self) -> &[u8] {
60    match self {
61      Self::String(s) => s.as_bytes(),
62      Self::Bytes(b) => b.as_bytes(),
63    }
64  }
65
66  pub fn into_cheap_copy(self) -> (Self, Self) {
67    match self {
68      Self::String(s) => {
69        let (s1, s2) = s.into_cheap_copy();
70        (Self::String(s1), Self::String(s2))
71      }
72      Self::Bytes(b) => {
73        let (b1, b2) = b.into_cheap_copy();
74        (Self::Bytes(b1), Self::Bytes(b2))
75      }
76    }
77  }
78}
79
80pub type ModuleCodeString = FastString;
81pub type ModuleName = FastString;
82
83/// Converts various string-like things into `ModuleName`.
84pub trait IntoModuleName {
85  fn into_module_name(self) -> ModuleName;
86}
87
88impl IntoModuleName for ModuleName {
89  fn into_module_name(self) -> ModuleName {
90    self
91  }
92}
93
94impl IntoModuleName for &'static str {
95  fn into_module_name(self) -> ModuleName {
96    ModuleName::from_static(self)
97  }
98}
99
100impl IntoModuleName for String {
101  fn into_module_name(self) -> ModuleName {
102    ModuleName::from(self)
103  }
104}
105
106impl IntoModuleName for Url {
107  fn into_module_name(self) -> ModuleName {
108    ModuleName::from(self)
109  }
110}
111
112impl IntoModuleName for FastStaticString {
113  fn into_module_name(self) -> ModuleName {
114    ModuleName::from(self)
115  }
116}
117
118/// Converts various string-like things into `ModuleCodeString`.
119pub trait IntoModuleCodeString {
120  fn into_module_code(self) -> ModuleCodeString;
121}
122
123impl IntoModuleCodeString for ModuleCodeString {
124  fn into_module_code(self) -> ModuleCodeString {
125    self
126  }
127}
128
129impl IntoModuleCodeString for &'static str {
130  fn into_module_code(self) -> ModuleCodeString {
131    ModuleCodeString::from_static(self)
132  }
133}
134
135impl IntoModuleCodeString for String {
136  fn into_module_code(self) -> ModuleCodeString {
137    ModuleCodeString::from(self)
138  }
139}
140
141impl IntoModuleCodeString for FastStaticString {
142  fn into_module_code(self) -> ModuleCodeString {
143    ModuleCodeString::from(self)
144  }
145}
146
147impl IntoModuleCodeString for Arc<str> {
148  fn into_module_code(self) -> ModuleCodeString {
149    ModuleCodeString::from(self)
150  }
151}
152
153#[derive(Debug, Hash, PartialEq, Eq)]
154pub enum ModuleCodeBytes {
155  /// Created from static data.
156  Static(&'static [u8]),
157
158  /// An owned chunk of data. Note that we use `Box` rather than `Vec` to avoid
159  /// the storage overhead.
160  Boxed(Box<[u8]>),
161
162  /// Code loaded from the `deno_graph` infrastructure.
163  Arc(Arc<[u8]>),
164}
165
166impl ModuleCodeBytes {
167  pub fn as_bytes(&self) -> &[u8] {
168    match self {
169      ModuleCodeBytes::Static(s) => s,
170      ModuleCodeBytes::Boxed(s) => s,
171      ModuleCodeBytes::Arc(s) => s,
172    }
173  }
174
175  pub fn to_vec(&self) -> Vec<u8> {
176    match self {
177      ModuleCodeBytes::Static(s) => s.to_vec(),
178      ModuleCodeBytes::Boxed(s) => s.to_vec(),
179      ModuleCodeBytes::Arc(s) => s.to_vec(),
180    }
181  }
182
183  /// Creates a cheap copy of this [`ModuleCodeBytes`], potentially transmuting
184  /// it to a faster form. Note that this is not a clone operation as it
185  /// consumes the old [`ModuleCodeBytes`].
186  pub fn into_cheap_copy(self) -> (Self, Self) {
187    match self {
188      ModuleCodeBytes::Boxed(b) => {
189        let b = Arc::<[u8]>::from(b);
190        (Self::Arc(b.clone()), Self::Arc(b))
191      }
192      _ => (self.try_clone().unwrap(), self),
193    }
194  }
195
196  /// If this [`ModuleCodeBytes`] is cheaply cloneable, returns a clone.
197  pub fn try_clone(&self) -> Option<Self> {
198    match &self {
199      Self::Static(b) => Some(Self::Static(b)),
200      Self::Boxed(_) => None,
201      Self::Arc(b) => Some(Self::Arc(b.clone())),
202    }
203  }
204}
205
206impl From<Arc<[u8]>> for ModuleCodeBytes {
207  fn from(value: Arc<[u8]>) -> Self {
208    Self::Arc(value)
209  }
210}
211
212impl From<Box<[u8]>> for ModuleCodeBytes {
213  fn from(value: Box<[u8]>) -> Self {
214    Self::Boxed(value)
215  }
216}
217
218impl From<&'static [u8]> for ModuleCodeBytes {
219  fn from(value: &'static [u8]) -> Self {
220    Self::Static(value)
221  }
222}
223
224/// Callback to validate import attributes. If the validation fails and exception
225/// should be thrown using `scope.throw_exception()`.
226pub type ValidateImportAttributesCb =
227  Box<dyn Fn(&mut v8::PinScope, &HashMap<String, String>)>;
228
229/// Callback to validate import attributes. If the validation fails and exception
230/// should be thrown using `scope.throw_exception()`.
231pub type CustomModuleEvaluationCb = Box<
232  dyn Fn(
233    &mut v8::PinScope,
234    Cow<'_, str>,
235    &FastString,
236    ModuleSourceCode,
237  ) -> Result<CustomModuleEvaluationKind, deno_error::JsErrorBox>,
238>;
239
240/// A callback to get the code cache for a script.
241/// (specifier, code) -> ...
242pub type EvalContextGetCodeCacheCb = Box<
243  dyn Fn(
244    &Url,
245    &v8::String,
246  ) -> Result<SourceCodeCacheInfo, deno_error::JsErrorBox>,
247>;
248
249/// Callback when the code cache is ready.
250/// (specifier, hash, data) -> ()
251pub type EvalContextCodeCacheReadyCb = Box<dyn Fn(Url, u64, &[u8])>;
252
253pub enum CustomModuleEvaluationKind {
254  /// This evaluation results in a single, "synthetic" module.
255  Synthetic(v8::Global<v8::Value>),
256
257  /// This evaluation results in creation of two modules:
258  ///  - a "computed" module - some JavaScript that most likely is rendered and
259  ///    uses the "synthetic" module - this module's ID is returned from
260  ///    [`new_module`] call.
261  ///  - a "synthetic" module - a kind of a helper module that abstracts
262  ///    the source of JS objects - this module is set up first.
263  ComputedAndSynthetic(
264    // Source code of computed module,
265    FastString,
266    // Synthetic module value
267    v8::Global<v8::Value>,
268    // Synthetic module type
269    ModuleType,
270  ),
271}
272
273#[derive(Debug)]
274pub(crate) enum ImportAttributesKind {
275  StaticImport,
276  DynamicImport,
277}
278
279pub(crate) fn parse_import_attributes<'s, 'i>(
280  scope: &mut v8::PinScope<'s, 'i>,
281  attributes: v8::Local<'s, v8::FixedArray>,
282  kind: ImportAttributesKind,
283) -> HashMap<String, String> {
284  let mut assertions: HashMap<String, String> = HashMap::default();
285
286  let assertions_per_line = match kind {
287    // For static imports, assertions are triples of (keyword, value and source offset)
288    // Also used in `module_resolve_callback`.
289    ImportAttributesKind::StaticImport => 3,
290    // For dynamic imports, assertions are tuples of (keyword, value)
291    ImportAttributesKind::DynamicImport => 2,
292  };
293  assert_eq!(attributes.length() % assertions_per_line, 0);
294  let no_of_assertions = attributes.length() / assertions_per_line;
295
296  for i in 0..no_of_assertions {
297    let assert_key = attributes.get(scope, assertions_per_line * i).unwrap();
298    let assert_key_val = v8::Local::<v8::Value>::try_from(assert_key).unwrap();
299    let assert_value = attributes
300      .get(scope, (assertions_per_line * i) + 1)
301      .unwrap();
302    let assert_value_val =
303      v8::Local::<v8::Value>::try_from(assert_value).unwrap();
304    assertions.insert(
305      assert_key_val.to_rust_string_lossy(scope),
306      assert_value_val.to_rust_string_lossy(scope),
307    );
308  }
309
310  assertions
311}
312
313pub(crate) fn get_requested_module_type_from_attributes(
314  attributes: &HashMap<String, String>,
315) -> RequestedModuleType {
316  let Some(ty) = attributes.get("type") else {
317    return RequestedModuleType::None;
318  };
319
320  match ty.as_str() {
321    "json" => RequestedModuleType::Json,
322    "text" => RequestedModuleType::Text,
323    "bytes" => RequestedModuleType::Bytes,
324    other => RequestedModuleType::Other(Cow::Owned(other.to_string())),
325  }
326}
327
328/// A type of module to be executed.
329///
330/// `deno_core` supports loading and executing JavaScript, Wasm and JSON modules,
331/// by default, but embedders can customize it further by providing
332/// [`CustomModuleEvaluationCb`].
333#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
334pub enum ModuleType {
335  JavaScript,
336  Wasm,
337  Json,
338  Text,
339  Bytes,
340  Other(Cow<'static, str>),
341}
342
343impl std::fmt::Display for ModuleType {
344  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
345    match self {
346      Self::JavaScript => write!(f, "JavaScript"),
347      Self::Wasm => write!(f, "Wasm"),
348      Self::Json => write!(f, "JSON"),
349      Self::Text => write!(f, "Text"),
350      Self::Bytes => write!(f, "Bytes"),
351      Self::Other(ty) => write!(f, "{}", ty),
352    }
353  }
354}
355
356impl ModuleType {
357  pub fn to_v8<'s, 'i>(
358    &self,
359    scope: &mut v8::PinScope<'s, 'i>,
360  ) -> v8::Local<'s, v8::Value> {
361    match self {
362      ModuleType::JavaScript => v8::Integer::new(scope, 0).into(),
363      ModuleType::Wasm => v8::Integer::new(scope, 1).into(),
364      ModuleType::Json => v8::Integer::new(scope, 2).into(),
365      ModuleType::Text => v8::Integer::new(scope, 3).into(),
366      ModuleType::Bytes => v8::Integer::new(scope, 4).into(),
367      ModuleType::Other(ty) => v8::String::new(scope, ty).unwrap().into(),
368    }
369  }
370
371  pub fn try_from_v8<'s, 'i>(
372    scope: &mut v8::PinScope<'s, 'i>,
373    value: v8::Local<'s, v8::Value>,
374  ) -> Option<Self> {
375    Some(if let Some(int) = value.to_integer(scope) {
376      match int.int32_value(scope).unwrap_or_default() {
377        0 => ModuleType::JavaScript,
378        1 => ModuleType::Wasm,
379        2 => ModuleType::Json,
380        3 => ModuleType::Text,
381        4 => ModuleType::Bytes,
382        _ => return None,
383      }
384    } else if let Ok(str) = v8::Local::<v8::String>::try_from(value) {
385      ModuleType::Other(Cow::Owned(str.to_rust_string_lossy(scope)))
386    } else {
387      return None;
388    })
389  }
390}
391
392#[derive(Debug)]
393pub struct SourceCodeCacheInfo {
394  pub hash: u64,
395  pub data: Option<Cow<'static, [u8]>>,
396}
397
398/// EsModule source code that will be loaded into V8.
399///
400/// Users can implement `Into<ModuleInfo>` for different file types that
401/// can be transpiled to valid EsModule.
402///
403/// Found module URL might be different from specified URL
404/// used for loading due to redirections (like HTTP 303).
405/// Eg. Both "`https://example.com/a.ts`" and
406/// "`https://example.com/b.ts`" may point to "`https://example.com/c.ts`"
407/// By keeping track of specified and found URL we can alias modules and avoid
408/// recompiling the same code 3 times.
409// TODO(bartlomieju): I have a strong opinion we should store all redirects
410// that happened; not only first and final target. It would simplify a lot
411// of things throughout the codebase otherwise we may end up requesting
412// intermediate redirects from file loader.
413// NOTE: This should _not_ be made #[derive(Clone)] unless we take some precautions to avoid excessive string copying.
414#[derive(Debug)]
415pub struct ModuleSource {
416  pub code: ModuleSourceCode,
417  pub module_type: ModuleType,
418  pub code_cache: Option<SourceCodeCacheInfo>,
419  module_url_specified: ModuleName,
420  /// If the module was found somewhere other than the specified address, this will be [`Some`].
421  module_url_found: Option<ModuleName>,
422}
423
424impl ModuleSource {
425  /// Create a [`ModuleSource`] without a redirect.
426  pub fn new(
427    module_type: impl Into<ModuleType>,
428    code: ModuleSourceCode,
429    specifier: &ModuleSpecifier,
430    code_cache: Option<SourceCodeCacheInfo>,
431  ) -> Self {
432    let module_url_specified = specifier.as_ref().to_owned().into();
433    Self {
434      code,
435      module_type: module_type.into(),
436      code_cache,
437      module_url_specified,
438      module_url_found: None,
439    }
440  }
441
442  /// Create a [`ModuleSource`] with a potential redirect. If the `specifier_found` parameter is the same as the
443  /// specifier, the code behaves the same was as `ModuleSource::new`.
444  pub fn new_with_redirect(
445    module_type: impl Into<ModuleType>,
446    code: ModuleSourceCode,
447    specifier: &ModuleSpecifier,
448    specifier_found: &ModuleSpecifier,
449    code_cache: Option<SourceCodeCacheInfo>,
450  ) -> Self {
451    let module_url_found = if specifier == specifier_found {
452      None
453    } else {
454      Some(specifier_found.as_ref().to_owned().into())
455    };
456    let module_url_specified = specifier.as_ref().to_owned().into();
457    Self {
458      code,
459      module_type: module_type.into(),
460      code_cache,
461      module_url_specified,
462      module_url_found,
463    }
464  }
465
466  #[cfg(test)]
467  pub fn for_test(code: &'static str, file: impl AsRef<str>) -> Self {
468    Self {
469      code: ModuleSourceCode::String(code.into_module_code()),
470      module_type: ModuleType::JavaScript,
471      code_cache: None,
472      module_url_specified: file.as_ref().to_owned().into(),
473      module_url_found: None,
474    }
475  }
476
477  /// If the `found` parameter is the same as the `specified` parameter, the code behaves the same was as `ModuleSource::for_test`.
478  #[cfg(test)]
479  pub fn for_test_with_redirect(
480    code: &'static str,
481    specified: impl AsRef<str>,
482    found: impl AsRef<str>,
483    code_cache: Option<SourceCodeCacheInfo>,
484  ) -> Self {
485    let specified = specified.as_ref().to_string();
486    let found = found.as_ref().to_string();
487    let found = if found == specified {
488      None
489    } else {
490      Some(found.into())
491    };
492    Self {
493      code: ModuleSourceCode::String(code.into_module_code()),
494      module_type: ModuleType::JavaScript,
495      code_cache,
496      module_url_specified: specified.into(),
497      module_url_found: found,
498    }
499  }
500
501  pub fn get_string_source(code: ModuleSourceCode) -> ModuleCodeString {
502    match code {
503      ModuleSourceCode::String(code) => code,
504      ModuleSourceCode::Bytes(bytes) => {
505        match String::from_utf8_lossy(bytes.as_bytes()) {
506          Cow::Borrowed(s) => ModuleCodeString::from(s.to_owned()),
507          Cow::Owned(s) => ModuleCodeString::from(s),
508        }
509      }
510    }
511  }
512
513  pub fn cheap_copy_code(&mut self) -> ModuleSourceCode {
514    let code = std::mem::replace(
515      &mut self.code,
516      ModuleSourceCode::Bytes(ModuleCodeBytes::Static(&[])),
517    );
518    let (code1, code2) = code.into_cheap_copy();
519    self.code = code1;
520    code2
521  }
522
523  pub fn cheap_copy_module_url_specified(&mut self) -> ModuleName {
524    let url = std::mem::replace(&mut self.module_url_specified, unsafe {
525      FastString::from_ascii_static_unchecked("")
526    });
527    let (url1, url2) = url.into_cheap_copy();
528    self.module_url_specified = url1;
529    url2
530  }
531
532  pub fn cheap_copy_module_url_found(&mut self) -> Option<ModuleName> {
533    let url = std::mem::take(&mut self.module_url_found)?;
534    let (url1, url2) = url.into_cheap_copy();
535    self.module_url_found = Some(url1);
536    Some(url2)
537  }
538}
539
540pub type ModuleSourceFuture =
541  dyn Future<Output = Result<ModuleSource, ModuleLoaderError>>;
542
543#[derive(Debug, Clone, Copy, PartialEq, Eq)]
544pub enum ResolutionKind {
545  /// This kind is used in only one situation: when a module is loaded via
546  /// `JsRuntime::load_main_module` and is the top-level module, ie. the one
547  /// passed as an argument to `JsRuntime::load_main_module`.
548  MainModule,
549  /// This kind is returned for all other modules during module load, that are
550  /// static imports.
551  Import,
552  /// This kind is returned for all modules that are loaded as a result of a
553  /// call to `import()` API (ie. top-level module as well as all its
554  /// dependencies, and any other `import()` calls from that load).
555  DynamicImport,
556}
557
558#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
559pub enum RequestedModuleType {
560  /// There was no attribute specified in the import statement.
561  ///
562  /// Example:
563  /// ```ignore
564  /// import foo from "./foo.js";
565  ///
566  /// const bar = await import("bar");
567  /// ```
568  None,
569
570  /// Example:
571  /// ```ignore
572  /// import jsonData from "./data.json" with { type: "json" };
573  ///
574  /// const jsonData2 = await import("./data2.json", { with { type: "json" } });
575  /// ```
576  Json,
577
578  /// Example:
579  /// ```ignore
580  /// import logText from "./log.txt" with { type: "text" };
581  ///
582  /// const logText = await import("./log.txt", { with { type: "text" } });
583  /// ```
584  Text,
585
586  /// Example:
587  /// ```ignore
588  /// import img from "./img.png" with { type: "bytes" };
589  ///
590  /// const img = await import("./img.png", { with { type: "bytes" } });
591  /// ```
592  Bytes,
593
594  /// An arbitrary module type. It is up to the embedder to handle (or deny) it.
595  /// If [`CustomModuleEvaluationCb`] was not passed when creating a runtime,
596  /// then all "other" module types cause an error to be returned.
597  ///
598  /// Example:
599  /// ```ignore
600  /// import url from "./style.css" with { type: "url" };
601  ///
602  /// const imgData = await import(`./images/${name}.png`, { with: { type: "image" }});
603  /// ```
604  Other(Cow<'static, str>),
605}
606
607impl RequestedModuleType {
608  pub fn to_v8<'s, 'i>(
609    &self,
610    scope: &mut v8::PinScope<'s, 'i>,
611  ) -> v8::Local<'s, v8::Value> {
612    match self {
613      RequestedModuleType::None => v8::Integer::new(scope, 0).into(),
614      RequestedModuleType::Json => v8::Integer::new(scope, 1).into(),
615      RequestedModuleType::Text => v8::Integer::new(scope, 2).into(),
616      RequestedModuleType::Bytes => v8::Integer::new(scope, 3).into(),
617      RequestedModuleType::Other(ty) => {
618        v8::String::new(scope, ty).unwrap().into()
619      }
620    }
621  }
622
623  pub fn try_from_v8<'s, 'i>(
624    scope: &mut v8::PinScope<'s, 'i>,
625    value: v8::Local<'s, v8::Value>,
626  ) -> Option<Self> {
627    Some(if let Some(int) = value.to_integer(scope) {
628      match int.int32_value(scope).unwrap_or_default() {
629        0 => RequestedModuleType::None,
630        1 => RequestedModuleType::Json,
631        2 => RequestedModuleType::Text,
632        3 => RequestedModuleType::Bytes,
633        _ => return None,
634      }
635    } else if let Ok(str) = v8::Local::<v8::String>::try_from(value) {
636      RequestedModuleType::Other(Cow::Owned(str.to_rust_string_lossy(scope)))
637    } else {
638      return None;
639    })
640  }
641
642  pub fn as_str(&self) -> Option<&str> {
643    match self {
644      RequestedModuleType::None => None,
645      RequestedModuleType::Json => Some("json"),
646      RequestedModuleType::Text => Some("text"),
647      RequestedModuleType::Bytes => Some("bytes"),
648      RequestedModuleType::Other(ty) => Some(ty),
649    }
650  }
651}
652
653impl AsRef<RequestedModuleType> for RequestedModuleType {
654  fn as_ref(&self) -> &RequestedModuleType {
655    self
656  }
657}
658
659// TODO(bartlomieju): this is questionable. I think we should remove it.
660impl PartialEq<ModuleType> for RequestedModuleType {
661  fn eq(&self, other: &ModuleType) -> bool {
662    match other {
663      ModuleType::JavaScript => self == &RequestedModuleType::None,
664      ModuleType::Wasm => self == &RequestedModuleType::None,
665      ModuleType::Json => self == &RequestedModuleType::Json,
666      ModuleType::Text => self == &RequestedModuleType::Text,
667      ModuleType::Bytes => self == &RequestedModuleType::Bytes,
668      ModuleType::Other(ty) => self == &RequestedModuleType::Other(ty.clone()),
669    }
670  }
671}
672
673impl From<ModuleType> for RequestedModuleType {
674  fn from(module_type: ModuleType) -> RequestedModuleType {
675    match module_type {
676      ModuleType::JavaScript => RequestedModuleType::None,
677      ModuleType::Wasm => RequestedModuleType::None,
678      ModuleType::Json => RequestedModuleType::Json,
679      ModuleType::Text => RequestedModuleType::Text,
680      ModuleType::Bytes => RequestedModuleType::Bytes,
681      ModuleType::Other(ty) => RequestedModuleType::Other(ty.clone()),
682    }
683  }
684}
685
686impl std::fmt::Display for RequestedModuleType {
687  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
688    match self {
689      Self::None => write!(f, "None"),
690      Self::Json => write!(f, "JSON"),
691      Self::Text => write!(f, "Text"),
692      Self::Bytes => write!(f, "Bytes"),
693      Self::Other(ty) => write!(f, "Other({ty})"),
694    }
695  }
696}
697
698/// Describes a request for a module as parsed from the source code.
699/// Usually executable (`JavaScriptOrWasm`) is used, except when an
700/// import assertions explicitly constrains an import to JSON, in
701/// which case this will have a `RequestedModuleType::Json`.
702#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
703pub(crate) struct ModuleReference {
704  pub specifier: ModuleSpecifier,
705  pub requested_module_type: RequestedModuleType,
706}
707
708#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
709pub(crate) enum ModuleImportPhase {
710  Evaluation,
711  Source,
712  Defer,
713}
714
715/// Describes a request for a module as parsed from the source code.
716/// Usually executable (`JavaScriptOrWasm`) is used, except when an
717/// import assertions explicitly constrains an import to JSON, in
718/// which case this will have a `RequestedModuleType::Json`.
719#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
720pub(crate) struct ModuleRequest {
721  pub reference: ModuleReference,
722  /// None if this is a root request.
723  pub specifier_key: Option<String>,
724  /// None if this is a root request.
725  pub referrer_source_offset: Option<i32>,
726  pub phase: ModuleImportPhase,
727}
728
729#[derive(Debug, PartialEq, Serialize, Deserialize)]
730pub(crate) struct ModuleInfo {
731  pub id: ModuleId,
732  pub main: bool,
733  pub name: ModuleName,
734  pub requests: Vec<ModuleRequest>,
735  pub module_type: ModuleType,
736}
737
738#[derive(Debug, thiserror::Error, deno_error::JsError)]
739#[class(generic)]
740pub enum ModuleConcreteError {
741  #[error(
742    "Trying to create \"main\" module ({new_module:?}), when one already exists ({main_module:?})"
743  )]
744  MainModuleAlreadyExists {
745    main_module: String,
746    new_module: String,
747  },
748  #[error("Unable to get code cache from unbound module script")]
749  UnboundModuleScriptCodeCache,
750  #[class(inherit)]
751  #[error("{0}")]
752  WasmParse(wasm_dep_analyzer::ParseError),
753  #[error("Source code for Wasm module must be provided as bytes")]
754  WasmNotBytes,
755  #[error("Failed to compile Wasm module '{0}'")]
756  WasmCompile(String),
757  #[error("Importing '{0}' modules is not supported")]
758  UnsupportedKind(String),
759  #[error("Source code for Bytes module must be provided as bytes")]
760  BytesNotBytes,
761}
762
763#[derive(Debug)]
764pub enum ModuleError {
765  Exception(v8::Global<v8::Value>),
766  Concrete(ModuleConcreteError),
767  Core(CoreError),
768}
769
770impl ModuleError {
771  pub fn into_error(
772    self,
773    scope: &mut v8::PinScope,
774    in_promise: bool,
775    clear_error: bool,
776  ) -> CoreError {
777    match self {
778      ModuleError::Exception(exception) => {
779        let exception = v8::Local::new(scope, exception);
780        CoreErrorKind::Js(exception_to_err(
781          scope,
782          exception,
783          in_promise,
784          clear_error,
785        ))
786        .into_box()
787      }
788      ModuleError::Core(error) => error,
789      ModuleError::Concrete(error) => CoreErrorKind::Module(error).into_box(),
790    }
791  }
792}
793
794impl From<ModuleConcreteError> for ModuleError {
795  fn from(value: ModuleConcreteError) -> Self {
796    ModuleError::Concrete(value)
797  }
798}