use crate::js::JsRuntime;
use crate::js::loader::{CoreModuleLoader, FsModuleLoader, ModuleLoader};
use crate::prelude::*;
use std::sync::LazyLock;
pub use es_module::*;
pub use import_map::*;
pub use module_map::*;
pub mod es_module;
pub mod import_map;
pub mod module_map;
pub type ModulePath = String;
pub type ModuleSource = String;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModuleStatus {
Fetching,
Resolving,
Duplicate,
Ready,
}
pub static CORE_MODULES: LazyLock<HashMap<&'static str, &'static str>> =
LazyLock::new(|| {
let modules = vec![
];
HashMap::from_iter(modules)
});
fn create_origin<'s>(
scope: &mut v8::HandleScope<'s, ()>,
name: &str,
is_module: bool,
) -> v8::ScriptOrigin<'s> {
let name = v8::String::new(scope, name).unwrap();
let source_map = v8::undefined(scope);
v8::ScriptOrigin::new(
scope,
name.into(),
0,
0,
false,
0,
Some(source_map.into()),
false,
false,
is_module,
None,
)
}
const CORE_MODULE_LOADER: CoreModuleLoader = CoreModuleLoader {};
const FS_MODULE_LOADER: FsModuleLoader = FsModuleLoader {};
fn _choose_module_loader(specifier: &str) -> &dyn ModuleLoader {
let is_core_module_import = CORE_MODULES.contains_key(specifier);
if is_core_module_import {
&CORE_MODULE_LOADER
} else {
&FS_MODULE_LOADER
}
}
pub fn resolve_import(
base: Option<&str>,
specifier: &str,
import_map: Option<ImportMap>,
) -> AnyResult<ModulePath> {
let specifier = match import_map {
Some(map) => map.lookup(specifier).unwrap_or_else(|| specifier.into()),
None => specifier.into(),
};
let resolver: &dyn ModuleLoader = _choose_module_loader(specifier.as_str());
resolver.resolve(base, &specifier)
}
pub fn load_import(
specifier: &str,
_skip_cache: bool,
) -> AnyResult<ModuleSource> {
let loader: &dyn ModuleLoader = _choose_module_loader(specifier);
loader.load(specifier)
}
pub async fn load_import_async(
specifier: &str,
skip_cache: bool,
) -> AnyResult<ModuleSource> {
load_import(specifier, skip_cache)
}
pub fn fetch_module<'a>(
scope: &mut v8::HandleScope<'a>,
filename: &str,
source: Option<&str>,
) -> Option<v8::Local<'a, v8::Module>> {
let origin = create_origin(scope, filename, true);
let source = match source {
Some(source) => source.into(),
None => load_import(filename, true).unwrap(),
};
if cfg!(debug_assertions) {
const MAX_SRC_LEN: usize = 100;
let src = if source.as_str().len() > MAX_SRC_LEN {
String::from(&source.as_str()[..MAX_SRC_LEN]) + "..."
} else {
String::from(source.as_str())
};
trace!("Fetch module, filename:{:?}, source:{:?}", filename, src);
}
let source = v8::String::new(scope, &source).unwrap();
let mut source = v8::script_compiler::Source::new(source, Some(&origin));
v8::script_compiler::compile_module(scope, &mut source)
}
pub fn fetch_module_tree<'a>(
scope: &mut v8::HandleScope<'a>,
filename: &str,
source: Option<&str>,
) -> Option<v8::Local<'a, v8::Module>> {
let module = match fetch_module(scope, filename, source) {
Some(module) => module,
None => {
return None;
}
};
let state_rc = JsRuntime::state(scope);
let module_ref = v8::Global::new(scope, module);
state_rc
.borrow_mut()
.module_map
.insert(filename, module_ref);
let requests = module.get_module_requests();
trace!("Get {} module requests", requests.length());
for i in 0..requests.length() {
let request = requests.get(scope, i).unwrap();
let request = v8::Local::<v8::ModuleRequest>::try_from(request).unwrap();
let specifier = request.get_specifier().to_rust_string_lossy(scope);
let specifier = resolve_import(Some(filename), &specifier, None).unwrap();
trace!(
"Resolved dependency modules, filename: {:?}, specifier: {:?}",
filename,
specifier.as_str(),
);
if !state_rc
.borrow()
.module_map
.index()
.contains_key(&specifier)
{
fetch_module_tree(scope, &specifier, None)?;
}
}
Some(module)
}