Skip to content

Add text and binary module types #1025

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions core/modules/loaders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pub trait ModuleLoader {
_module_specifier: &ModuleSpecifier,
_maybe_referrer: Option<String>,
_is_dyn_import: bool,
_requested_module_type: RequestedModuleType,
) -> Pin<Box<dyn Future<Output = Result<(), Error>>>> {
async { Ok(()) }.boxed_local()
}
Expand Down Expand Up @@ -267,6 +268,7 @@ impl ModuleLoader for ExtModuleLoader {
_specifier: &ModuleSpecifier,
_maybe_referrer: Option<String>,
_is_dyn_import: bool,
_requested_module_type: RequestedModuleType,
) -> Pin<Box<dyn Future<Output = Result<(), Error>>>> {
async { Ok(()) }.boxed_local()
}
Expand Down Expand Up @@ -322,6 +324,7 @@ impl ModuleLoader for LazyEsmModuleLoader {
_specifier: &ModuleSpecifier,
_maybe_referrer: Option<String>,
_is_dyn_import: bool,
_requested_module_type: RequestedModuleType,
) -> Pin<Box<dyn Future<Output = Result<(), Error>>>> {
async { Ok(()) }.boxed_local()
}
Expand Down Expand Up @@ -516,11 +519,15 @@ impl<L: ModuleLoader> ModuleLoader for TestingModuleLoader<L> {
module_specifier: &ModuleSpecifier,
maybe_referrer: Option<String>,
is_dyn_import: bool,
requested_module_type: RequestedModuleType,
) -> Pin<Box<dyn Future<Output = Result<(), Error>>>> {
self.prepare_count.set(self.prepare_count.get() + 1);
self
.loader
.prepare_load(module_specifier, maybe_referrer, is_dyn_import)
self.loader.prepare_load(
module_specifier,
maybe_referrer,
is_dyn_import,
requested_module_type,
)
}

fn finish_load(&self) {
Expand Down
58 changes: 58 additions & 0 deletions core/modules/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,18 @@ impl ModuleMap {
let code = ModuleSource::get_string_source(code);
self.new_json_module(scope, module_url_found, code)?
}
ModuleType::Text => {
let code = ModuleSource::get_string_source(code);
self.new_text_module(scope, module_url_found, code)?
}
ModuleType::Binary => {
let ModuleSourceCode::Bytes(code) = code else {
return Err(ModuleError::Other(generic_error(
"Source code for Binary module must be provided as bytes",
)));
};
self.new_binary_module(scope, module_url_found, code)?
}
ModuleType::Other(module_type) => {
let state = JsRuntime::state_from(scope);
let custom_module_evaluation_cb =
Expand Down Expand Up @@ -828,6 +840,52 @@ impl ModuleMap {
self.new_synthetic_module(tc_scope, name, ModuleType::Json, exports)
}

pub(crate) fn new_text_module(
&self,
scope: &mut v8::HandleScope,
name: impl IntoModuleName,
code: impl IntoModuleCodeString,
) -> Result<ModuleId, ModuleError> {
let name = name.into_module_name();
let code = code.into_module_code();
let source_str = v8::String::new_from_utf8(
scope,
strip_bom(code.as_bytes()),
v8::NewStringType::Normal,
)
.unwrap();
let source_str_local = v8::Local::new(scope, source_str);
let source_value_local = v8::Local::<v8::Value>::from(source_str_local);
let exports = vec![(ascii_str!("default"), source_value_local)];
self.new_synthetic_module(scope, name, ModuleType::Text, exports)
}

pub(crate) fn new_binary_module(
&self,
scope: &mut v8::HandleScope,
name: impl IntoModuleName,
code: ModuleCodeBytes,
) -> Result<ModuleId, ModuleError> {
let name = name.into_module_name();
let backing_store = match code {
// TODO: can we more efficiently reuse the existing storage with a future immutable arraybuffer variant?
ModuleCodeBytes::Static(bytes) => {
v8::ArrayBuffer::new_backing_store_from_vec(bytes.to_vec())
}
ModuleCodeBytes::Boxed(bytes) => {
v8::ArrayBuffer::new_backing_store_from_boxed_slice(bytes)
}
ModuleCodeBytes::Arc(bytes) => {
v8::ArrayBuffer::new_backing_store_from_vec(bytes.to_vec())
}
};
let source_arraybuffer =
v8::ArrayBuffer::with_backing_store(scope, &backing_store.make_shared());
let source_value_local = v8::Local::<v8::Value>::from(source_arraybuffer);
let exports = vec![(ascii_str!("default"), source_value_local)];
self.new_synthetic_module(scope, name, ModuleType::Binary, exports)
}

pub(crate) fn instantiate_module(
&self,
scope: &mut v8::HandleScope,
Expand Down
26 changes: 25 additions & 1 deletion core/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ pub enum ModuleType {
JavaScript,
Wasm,
Json,
Text,
Binary,
Other(Cow<'static, str>),
}

Expand All @@ -313,6 +315,8 @@ impl std::fmt::Display for ModuleType {
Self::JavaScript => write!(f, "JavaScript"),
Self::Wasm => write!(f, "Wasm"),
Self::Json => write!(f, "JSON"),
Self::Text => write!(f, "Text"),
Self::Binary => write!(f, "Binary"),
Self::Other(ty) => write!(f, "{}", ty),
}
}
Expand All @@ -327,6 +331,8 @@ impl ModuleType {
ModuleType::JavaScript => v8::Integer::new(scope, 0).into(),
ModuleType::Wasm => v8::Integer::new(scope, 1).into(),
ModuleType::Json => v8::Integer::new(scope, 2).into(),
ModuleType::Text => v8::Integer::new(scope, 3).into(),
ModuleType::Binary => v8::Integer::new(scope, 4).into(),
ModuleType::Other(ty) => v8::String::new(scope, ty).unwrap().into(),
}
}
Expand All @@ -340,6 +346,8 @@ impl ModuleType {
0 => ModuleType::JavaScript,
1 => ModuleType::Wasm,
2 => ModuleType::Json,
3 => ModuleType::Text,
4 => ModuleType::Binary,
_ => return None,
}
} else if let Ok(str) = v8::Local::<v8::String>::try_from(value) {
Expand Down Expand Up @@ -511,7 +519,7 @@ pub enum RequestedModuleType {
/// ```ignore
/// import jsonData from "./data.json" with { type: "json" };
///
/// const jsonData2 = await import"./data2.json", { with { type: "json" } });
/// const jsonData2 = await import("./data2.json", { with { type: "json" } });
/// ```
Json,

Expand Down Expand Up @@ -558,6 +566,14 @@ impl RequestedModuleType {
return None;
})
}

pub fn as_str(&self) -> Option<&str> {
match self {
RequestedModuleType::None => None,
RequestedModuleType::Json => Some("json"),
RequestedModuleType::Other(ty) => Some(ty),
}
}
}

impl AsRef<RequestedModuleType> for RequestedModuleType {
Expand All @@ -573,6 +589,12 @@ impl PartialEq<ModuleType> for RequestedModuleType {
ModuleType::JavaScript => self == &RequestedModuleType::None,
ModuleType::Wasm => self == &RequestedModuleType::None,
ModuleType::Json => self == &RequestedModuleType::Json,
ModuleType::Text => {
self == &RequestedModuleType::Other(Cow::Borrowed("text"))
}
ModuleType::Binary => {
self == &RequestedModuleType::Other(Cow::Borrowed("binary"))
}
ModuleType::Other(ty) => self == &RequestedModuleType::Other(ty.clone()),
}
}
Expand All @@ -584,6 +606,8 @@ impl From<ModuleType> for RequestedModuleType {
ModuleType::JavaScript => RequestedModuleType::None,
ModuleType::Wasm => RequestedModuleType::None,
ModuleType::Json => RequestedModuleType::Json,
ModuleType::Text => RequestedModuleType::Other(Cow::Borrowed("text")),
ModuleType::Binary => RequestedModuleType::Other(Cow::Borrowed("binary")),
ModuleType::Other(ty) => RequestedModuleType::Other(ty.clone()),
}
}
Expand Down
27 changes: 21 additions & 6 deletions core/modules/recursive_load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,35 +150,50 @@ impl RecursiveModuleLoad {
}

pub(crate) async fn prepare(&self) -> Result<(), Error> {
let (module_specifier, maybe_referrer) = match self.init {
let (module_specifier, maybe_referrer, requested_module_type) = match self
.init
{
LoadInit::Main(ref specifier) => {
let spec = self.module_map_rc.resolve(
specifier,
".",
ResolutionKind::MainModule,
)?;
(spec, None)
(spec, None, RequestedModuleType::None)
}
LoadInit::Side(ref specifier) => {
let spec =
self
.module_map_rc
.resolve(specifier, ".", ResolutionKind::Import)?;
(spec, None)
(spec, None, RequestedModuleType::None)
}
LoadInit::DynamicImport(ref specifier, ref referrer, _) => {
LoadInit::DynamicImport(
ref specifier,
ref referrer,
ref requested_module_type,
) => {
let spec = self.module_map_rc.resolve(
specifier,
referrer,
ResolutionKind::DynamicImport,
)?;
(spec, Some(referrer.to_string()))
(
spec,
Some(referrer.to_string()),
requested_module_type.clone(),
)
}
};

self
.loader
.prepare_load(&module_specifier, maybe_referrer, self.is_dynamic_import())
.prepare_load(
&module_specifier,
maybe_referrer,
self.is_dynamic_import(),
requested_module_type,
)
.await
}

Expand Down
Loading