Skip to main content

rquickjs_core/loader/
bundle.rs

1//! Utilities for embedding JS modules.
2
3use super::{util::resolve_simple, ImportAttributes, Loader, Resolver};
4use crate::{Ctx, Error, Module, Result};
5use alloc::string::String;
6use core::ops::Deref;
7
8/// The module data which contains bytecode
9///
10/// This trait needed because the modules potentially can contain any kind of data like a typing (for TypeScript) or metadata.
11pub trait HasByteCode<'bc> {
12    fn get_bytecode(&self) -> &'bc [u8];
13}
14
15impl<'bc> HasByteCode<'bc> for &'bc [u8] {
16    fn get_bytecode(&self) -> &'bc [u8] {
17        self
18    }
19}
20
21/// The alias for compiled modules represented as a static const arrays
22///
23/// The element is a tuple of `(module_name, module_data)`.
24pub type ScaBundleData<D> = &'static [(&'static str, D)];
25
26#[cfg(feature = "phf")]
27/// The alias for compiled modules represented as a perfect hash maps
28///
29/// The key is a module name and the value is a module data.
30pub type PhfBundleData<D> = &'static phf::Map<&'static str, D>;
31
32/// The resolver and loader for bundles of compiled modules
33#[derive(Debug, Clone, Copy)]
34pub struct Bundle<T>(pub T);
35
36impl<T> Deref for Bundle<T> {
37    type Target = T;
38
39    fn deref(&self) -> &Self::Target {
40        &self.0
41    }
42}
43
44impl<D> Resolver for Bundle<ScaBundleData<D>> {
45    fn resolve<'js>(
46        &mut self,
47        _ctx: &Ctx<'js>,
48        base: &str,
49        name: &str,
50        _attributes: Option<ImportAttributes<'js>>,
51    ) -> Result<String> {
52        let path = resolve_simple(base, name);
53        if self.iter().any(|(name, _)| *name == path) {
54            Ok(path)
55        } else {
56            Err(Error::new_resolving(base, name))
57        }
58    }
59}
60
61#[cfg(feature = "phf")]
62impl<D> Resolver for Bundle<PhfBundleData<D>> {
63    fn resolve<'js>(
64        &mut self,
65        _ctx: &Ctx<'js>,
66        base: &str,
67        name: &str,
68        _attributes: Option<ImportAttributes<'js>>,
69    ) -> Result<String> {
70        let path = resolve_simple(base, name);
71        if self.contains_key(path.as_str()) {
72            Ok(path)
73        } else {
74            Err(Error::new_resolving(base, name))
75        }
76    }
77}
78
79impl<D> Loader for Bundle<ScaBundleData<D>>
80where
81    D: HasByteCode<'static>,
82{
83    fn load<'js>(
84        &mut self,
85        ctx: &Ctx<'js>,
86        name: &str,
87        _attributes: Option<crate::loader::ImportAttributes<'js>>,
88    ) -> Result<Module<'js>> {
89        if let Some((_, x)) = self.iter().find(|(module_name, _)| *module_name == name) {
90            let module = unsafe { Module::load(ctx.clone(), x.get_bytecode())? };
91            return Ok(module);
92        }
93        Err(Error::new_loading(name))
94    }
95}
96
97#[cfg(feature = "phf")]
98impl<D> Loader for Bundle<PhfBundleData<D>>
99where
100    D: HasByteCode<'static>,
101{
102    fn load<'js>(
103        &mut self,
104        ctx: &Ctx<'js>,
105        name: &str,
106        _attributes: Option<crate::loader::ImportAttributes<'js>>,
107    ) -> Result<Module<'js>> {
108        if let Some(x) = self.get(name) {
109            let module = unsafe { Module::load(ctx.clone(), x.get_bytecode())? };
110            return Ok(module);
111        }
112        Err(Error::new_loading(name))
113    }
114}