1use 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#[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
83pub 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
118pub 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 Static(&'static [u8]),
157
158 Boxed(Box<[u8]>),
161
162 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 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 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
224pub type ValidateImportAttributesCb =
227 Box<dyn Fn(&mut v8::PinScope, &HashMap<String, String>)>;
228
229pub 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
240pub type EvalContextGetCodeCacheCb = Box<
243 dyn Fn(
244 &Url,
245 &v8::String,
246 ) -> Result<SourceCodeCacheInfo, deno_error::JsErrorBox>,
247>;
248
249pub type EvalContextCodeCacheReadyCb = Box<dyn Fn(Url, u64, &[u8])>;
252
253pub enum CustomModuleEvaluationKind {
254 Synthetic(v8::Global<v8::Value>),
256
257 ComputedAndSynthetic(
264 FastString,
266 v8::Global<v8::Value>,
268 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 ImportAttributesKind::StaticImport => 3,
290 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#[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#[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 module_url_found: Option<ModuleName>,
422}
423
424impl ModuleSource {
425 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 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 #[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 MainModule,
549 Import,
552 DynamicImport,
556}
557
558#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
559pub enum RequestedModuleType {
560 None,
569
570 Json,
577
578 Text,
585
586 Bytes,
593
594 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
659impl 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#[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#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
720pub(crate) struct ModuleRequest {
721 pub reference: ModuleReference,
722 pub specifier_key: Option<String>,
724 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}