diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/extensions.rs | 32 | ||||
-rw-r--r-- | core/lib.rs | 1 | ||||
-rw-r--r-- | core/modules.rs | 83 | ||||
-rw-r--r-- | core/runtime.rs | 21 | ||||
-rw-r--r-- | core/snapshot_util.rs | 46 |
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()); |