summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/extensions.rs32
-rw-r--r--core/lib.rs1
-rw-r--r--core/modules.rs83
-rw-r--r--core/runtime.rs21
-rw-r--r--core/snapshot_util.rs46
5 files changed, 127 insertions, 56 deletions
diff --git a/core/extensions.rs b/core/extensions.rs
index e08e8c566..16cca924d 100644
--- a/core/extensions.rs
+++ b/core/extensions.rs
@@ -6,6 +6,7 @@ use std::rc::Rc;
use std::task::Context;
use v8::fast_api::FastFunction;
+#[derive(Clone, Debug)]
pub struct ExtensionFileSource {
pub specifier: String,
pub code: &'static str,
@@ -244,8 +245,9 @@ impl ExtensionBuilder {
}
}
}
-/// Helps embed JS files in an extension. Returns Vec<(&'static str, &'static str)>
-/// representing the filename and source code.
+
+/// Helps embed JS files in an extension. Returns a vector of
+/// `ExtensionFileSource`, that represent the filename and source code.
///
/// Example:
/// ```ignore
@@ -265,3 +267,29 @@ macro_rules! include_js_files {
]
};
}
+
+/// Helps embed JS files in an extension. Returns a vector of
+/// `ExtensionFileSource`, that represent the filename and source code.
+/// Additional "dir" option is required, that specifies which directory in the
+/// crate root contains the listed files. "dir" option will be prepended to
+/// each file name.
+///
+/// Example:
+/// ```ignore
+/// include_js_files_dir!(
+/// dir "example",
+/// "01_hello.js",
+/// "02_goodbye.js",
+/// )
+/// ```
+#[macro_export]
+macro_rules! include_js_files_dir {
+ (dir $dir:literal, $($file:literal,)+) => {
+ vec![
+ $($crate::ExtensionFileSource {
+ specifier: concat!($dir, "/", $file).to_string(),
+ code: include_str!(concat!($dir, "/", $file)),
+ },)+
+ ]
+ };
+}
diff --git a/core/lib.rs b/core/lib.rs
index 86c432d43..a77701339 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -75,6 +75,7 @@ pub use crate::module_specifier::ModuleSpecifier;
pub use crate::module_specifier::DUMMY_SPECIFIER;
pub use crate::modules::FsModuleLoader;
pub use crate::modules::InternalModuleLoader;
+pub use crate::modules::InternalModuleLoaderCb;
pub use crate::modules::ModuleId;
pub use crate::modules::ModuleLoader;
pub use crate::modules::ModuleSource;
diff --git a/core/modules.rs b/core/modules.rs
index da0d8cba3..b604e8c13 100644
--- a/core/modules.rs
+++ b/core/modules.rs
@@ -2,6 +2,7 @@
use crate::bindings;
use crate::error::generic_error;
+use crate::extensions::ExtensionFileSource;
use crate::module_specifier::ModuleSpecifier;
use crate::resolve_import;
use crate::resolve_url;
@@ -312,13 +313,38 @@ pub(crate) fn resolve_helper(
loader.resolve(specifier, referrer, kind)
}
-pub struct InternalModuleLoader(Rc<dyn ModuleLoader>);
+/// Function that can be passed to the `InternalModuleLoader` that allows to
+/// transpile sources before passing to V8.
+pub type InternalModuleLoaderCb =
+ Box<dyn Fn(&ExtensionFileSource) -> Result<String, Error>>;
+
+pub struct InternalModuleLoader {
+ module_loader: Rc<dyn ModuleLoader>,
+ esm_sources: Vec<ExtensionFileSource>,
+ maybe_load_callback: Option<InternalModuleLoaderCb>,
+}
+
+impl Default for InternalModuleLoader {
+ fn default() -> Self {
+ Self {
+ module_loader: Rc::new(NoopModuleLoader),
+ esm_sources: vec![],
+ maybe_load_callback: None,
+ }
+ }
+}
impl InternalModuleLoader {
- pub fn new(module_loader: Option<Rc<dyn ModuleLoader>>) -> Self {
- InternalModuleLoader(
- module_loader.unwrap_or_else(|| Rc::new(NoopModuleLoader)),
- )
+ pub fn new(
+ module_loader: Option<Rc<dyn ModuleLoader>>,
+ esm_sources: Vec<ExtensionFileSource>,
+ maybe_load_callback: Option<InternalModuleLoaderCb>,
+ ) -> Self {
+ InternalModuleLoader {
+ module_loader: module_loader.unwrap_or_else(|| Rc::new(NoopModuleLoader)),
+ esm_sources,
+ maybe_load_callback,
+ }
}
}
@@ -343,7 +369,7 @@ impl ModuleLoader for InternalModuleLoader {
}
}
- self.0.resolve(specifier, referrer, kind)
+ self.module_loader.resolve(specifier, referrer, kind)
}
fn load(
@@ -352,7 +378,46 @@ impl ModuleLoader for InternalModuleLoader {
maybe_referrer: Option<ModuleSpecifier>,
is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
- self.0.load(module_specifier, maybe_referrer, is_dyn_import)
+ if module_specifier.scheme() != "internal" {
+ return self.module_loader.load(
+ module_specifier,
+ maybe_referrer,
+ is_dyn_import,
+ );
+ }
+
+ let specifier = module_specifier.to_string();
+ let maybe_file_source = self
+ .esm_sources
+ .iter()
+ .find(|file_source| file_source.specifier == module_specifier.as_str());
+
+ if let Some(file_source) = maybe_file_source {
+ let result = if let Some(load_callback) = &self.maybe_load_callback {
+ load_callback(file_source)
+ } else {
+ Ok(file_source.code.to_string())
+ };
+
+ return async move {
+ let code = result?;
+ let source = ModuleSource {
+ code: code.into_bytes().into_boxed_slice(),
+ module_type: ModuleType::JavaScript,
+ module_url_specified: specifier.clone(),
+ module_url_found: specifier.clone(),
+ };
+ Ok(source)
+ }
+ .boxed_local();
+ }
+
+ async move {
+ Err(generic_error(format!(
+ "Cannot find internal module source for specifier {specifier}"
+ )))
+ }
+ .boxed_local()
}
fn prepare_load(
@@ -366,7 +431,7 @@ impl ModuleLoader for InternalModuleLoader {
return async { Ok(()) }.boxed_local();
}
- self.0.prepare_load(
+ self.module_loader.prepare_load(
op_state,
module_specifier,
maybe_referrer,
@@ -2639,7 +2704,7 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
#[test]
fn internal_module_loader() {
- let loader = InternalModuleLoader::new(None);
+ let loader = InternalModuleLoader::default();
assert!(loader
.resolve("internal:foo", "internal:bar", ResolutionKind::Import)
.is_ok());
diff --git a/core/runtime.rs b/core/runtime.rs
index 377137a01..d922d0ca5 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -8,6 +8,7 @@ use crate::extensions::OpDecl;
use crate::extensions::OpEventLoopFn;
use crate::inspector::JsRuntimeInspector;
use crate::module_specifier::ModuleSpecifier;
+use crate::modules::InternalModuleLoaderCb;
use crate::modules::ModuleError;
use crate::modules::ModuleId;
use crate::modules::ModuleLoadId;
@@ -19,6 +20,7 @@ use crate::ops::*;
use crate::source_map::SourceMapCache;
use crate::source_map::SourceMapGetter;
use crate::Extension;
+use crate::ExtensionFileSource;
use crate::NoopModuleLoader;
use crate::OpMiddlewareFn;
use crate::OpResult;
@@ -270,6 +272,11 @@ pub struct RuntimeOptions {
/// The snapshot is deterministic and uses predictable random numbers.
pub will_snapshot: bool,
+ /// An optional callback that will be called for each module that is loaded
+ /// during snapshotting. This callback can be used to transpile source on the
+ /// fly, during snapshotting, eg. to transpile TypeScript to JavaScript.
+ pub snapshot_module_load_cb: Option<InternalModuleLoaderCb>,
+
/// Isolate creation parameters.
pub create_params: Option<v8::CreateParams>,
@@ -607,8 +614,16 @@ impl JsRuntime {
};
let loader = if snapshot_options != SnapshotOptions::Load {
+ let esm_sources = options
+ .extensions_with_js
+ .iter()
+ .flat_map(|ext| ext.get_esm_sources().to_owned())
+ .collect::<Vec<ExtensionFileSource>>();
+
Rc::new(crate::modules::InternalModuleLoader::new(
options.module_loader,
+ esm_sources,
+ options.snapshot_module_load_cb,
))
} else {
options
@@ -818,13 +833,13 @@ impl JsRuntime {
let extensions = std::mem::take(&mut self.extensions_with_js);
for ext in &extensions {
{
- let js_files = ext.get_esm_sources();
- for file_source in js_files {
+ let esm_files = ext.get_esm_sources();
+ for file_source in esm_files {
futures::executor::block_on(async {
let id = self
.load_side_module(
&ModuleSpecifier::parse(&file_source.specifier)?,
- Some(file_source.code.to_string()),
+ None,
)
.await?;
let receiver = self.mod_evaluate(id);
diff --git a/core/snapshot_util.rs b/core/snapshot_util.rs
index 8daaa9866..0a1793b51 100644
--- a/core/snapshot_util.rs
+++ b/core/snapshot_util.rs
@@ -1,12 +1,11 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-use anyhow::Context;
use std::path::Path;
use std::path::PathBuf;
use crate::Extension;
+use crate::InternalModuleLoaderCb;
use crate::JsRuntime;
-use crate::ModuleSpecifier;
use crate::RuntimeOptions;
use crate::Snapshot;
@@ -18,57 +17,20 @@ pub struct CreateSnapshotOptions {
pub startup_snapshot: Option<Snapshot>,
pub extensions: Vec<Extension>,
pub extensions_with_js: Vec<Extension>,
- pub additional_files: Vec<PathBuf>,
- pub additional_esm_files: Vec<PathBuf>,
pub compression_cb: Option<Box<CompressionCb>>,
+ pub snapshot_module_load_cb: Option<InternalModuleLoaderCb>,
}
pub fn create_snapshot(create_snapshot_options: CreateSnapshotOptions) {
- let mut js_runtime = JsRuntime::new(RuntimeOptions {
+ let js_runtime = JsRuntime::new(RuntimeOptions {
will_snapshot: true,
startup_snapshot: create_snapshot_options.startup_snapshot,
extensions: create_snapshot_options.extensions,
extensions_with_js: create_snapshot_options.extensions_with_js,
+ snapshot_module_load_cb: create_snapshot_options.snapshot_module_load_cb,
..Default::default()
});
- // TODO(nayeemrmn): https://github.com/rust-lang/cargo/issues/3946 to get the
- // workspace root.
- let display_root = Path::new(create_snapshot_options.cargo_manifest_dir)
- .parent()
- .unwrap();
- for file in create_snapshot_options.additional_files {
- let display_path = file.strip_prefix(display_root).unwrap_or(&file);
- let display_path_str = display_path.display().to_string();
- js_runtime
- .execute_script(
- &("internal:".to_string() + &display_path_str.replace('\\', "/")),
- &std::fs::read_to_string(&file).unwrap(),
- )
- .unwrap();
- }
- for file in create_snapshot_options.additional_esm_files {
- let display_path = file.strip_prefix(display_root).unwrap_or(&file);
- let display_path_str = display_path.display().to_string();
-
- let filename =
- &("internal:".to_string() + &display_path_str.replace('\\', "/"));
-
- futures::executor::block_on(async {
- let id = js_runtime
- .load_side_module(
- &ModuleSpecifier::parse(filename)?,
- Some(std::fs::read_to_string(&file)?),
- )
- .await?;
- let receiver = js_runtime.mod_evaluate(id);
- js_runtime.run_event_loop(false).await?;
- receiver.await?
- })
- .with_context(|| format!("Couldn't execute '{}'", file.display()))
- .unwrap();
- }
-
let snapshot = js_runtime.snapshot();
let snapshot_slice: &[u8] = &snapshot;
println!("Snapshot size: {}", snapshot_slice.len());