diff --git a/src-tauri/plugins/hello-world.js b/src-tauri/plugins/hello-world.js deleted file mode 100644 index 192b2fb8..00000000 --- a/src-tauri/plugins/hello-world.js +++ /dev/null @@ -1 +0,0 @@ -sayHello('Plugin'); diff --git a/src-tauri/plugins/hello-world/hello.js b/src-tauri/plugins/hello-world/hello.js new file mode 100644 index 00000000..1e784c8a --- /dev/null +++ b/src-tauri/plugins/hello-world/hello.js @@ -0,0 +1,3 @@ +export function hello() { + sayHello('Plugin'); +} diff --git a/src-tauri/plugins/hello-world/index.js b/src-tauri/plugins/hello-world/index.js new file mode 100644 index 00000000..d84dec2f --- /dev/null +++ b/src-tauri/plugins/hello-world/index.js @@ -0,0 +1,5 @@ +import { hello } from './hello.js'; + +export function entrypoint() { + hello(); +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index db702f81..d76a97d4 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -32,7 +32,7 @@ use window_ext::TrafficLightWindowExt; mod models; mod render; mod window_ext; -mod plugins; +mod plugin; #[derive(serde::Serialize)] pub struct CustomResponse { @@ -689,7 +689,7 @@ fn main() { let w = create_window(app_handle, None); w.restore_state(StateFlags::all()) .expect("Failed to restore window state"); - plugins::test_plugins(&app_handle); + plugin::test_plugins(&app_handle); } // ExitRequested { api, .. } => { diff --git a/src-tauri/src/plugin.rs b/src-tauri/src/plugin.rs new file mode 100644 index 00000000..93184e35 --- /dev/null +++ b/src-tauri/src/plugin.rs @@ -0,0 +1,97 @@ +use boa_engine::{ + js_string, + module::{ModuleLoader, SimpleModuleLoader}, + property::Attribute, + Context, JsArgs, JsNativeError, JsValue, Module, NativeFunction, Source, +}; +use boa_runtime::Console; +use tauri::AppHandle; + +pub fn test_plugins(app_handle: &AppHandle) { + let plugin_dir = app_handle + .path_resolver() + .resolve_resource("plugins/hello-world") + .expect("failed to resolve plugin directory resource"); + let plugin_entry_file = app_handle + .path_resolver() + .resolve_resource("plugins/hello-world/index.js") + .expect("failed to resolve plugin entry point resource"); + + // Module loader for the specific plugin + let loader = &SimpleModuleLoader::new(plugin_dir).expect("failed to create module loader"); + let dyn_loader: &dyn ModuleLoader = loader; + + let context = &mut Context::builder() + .module_loader(dyn_loader) + .build() + .expect("failed to create context"); + + add_runtime(context); + add_globals(context); + + let source = Source::from_filepath(&plugin_entry_file).expect("Error opening file"); + + // Can also pass a `Some(realm)` if you need to execute the module in another realm. + let module = Module::parse(source, None, context).expect("failed to parse module"); + + // Insert parsed entrypoint into the module loader + // TODO: Is this needed if loaded from file already? + loader.insert(plugin_entry_file, module.clone()); + + let _promise_result = module + .load_link_evaluate(context) + .expect("failed to evaluate module"); + + // Very important to push forward the job queue after queueing promises. + context.run_jobs(); + + // // Checking if the final promise didn't return an error. + // match promise_result.state() { + // PromiseState::Pending => return Err("module didn't execute!".into()), + // PromiseState::Fulfilled(v) => { + // assert_eq!(v, JsValue::undefined()) + // } + // PromiseState::Rejected(err) => { + // return Err(JsError::from_opaque(err).try_native(context)?.into()) + // } + // } + + let namespace = module.namespace(context); + + let entrypoint_fn = namespace + .get(js_string!("entrypoint"), context) + .expect("failed to get entrypoint") + .as_callable() + .cloned() + .ok_or_else(|| JsNativeError::typ().with_message("export wasn't a function!")) + .expect("Failed to get entrypoint"); + + // Actually call the entrypoint function + let _result = entrypoint_fn + .call(&JsValue::undefined(), &[], context) + .expect("Failed to call entrypoint"); +} + +fn add_runtime(context: &mut Context<'_>) { + let console = Console::init(context); + context + .register_global_property(js_string!(Console::NAME), console, Attribute::all()) + .expect("the console builtin shouldn't exist"); +} + +fn add_globals(context: &mut Context<'_>) { + context + .register_global_builtin_callable( + "sayHello", + 1, + NativeFunction::from_fn_ptr(|_, args, context| { + let value: String = args + .get_or_undefined(0) + .try_js_into(context) + .expect("failed to convert arg"); + println!("Hello {}!", value); + Ok(value.into()) + }), + ) + .expect("failed to register global"); +} diff --git a/src-tauri/src/plugins.rs b/src-tauri/src/plugins.rs deleted file mode 100644 index de0747d6..00000000 --- a/src-tauri/src/plugins.rs +++ /dev/null @@ -1,44 +0,0 @@ -use boa_engine::{ - js_string, property::Attribute, Context, JsArgs, NativeFunction, Source, -}; -use boa_runtime::Console; -use tauri::AppHandle; - -pub fn test_plugins(app_handle: &AppHandle) { - let file = app_handle - .path_resolver() - .resolve_resource("plugins/hello-world.js") - .expect("failed to resolve resource"); - let src = Source::from_filepath(&file).expect("Error opening file"); - - // Instantiate the execution context - let mut context = Context::default(); - add_runtime(&mut context); - - // Add globals - context - .register_global_builtin_callable( - "sayHello", - 1, - NativeFunction::from_fn_ptr(|_, args, context| { - let value: String = args - .get_or_undefined(0) - .try_js_into(context) - .expect("failed to convert arg"); - - println!("Hello {}!", value); - - Ok(value.into()) - }), - ) - .expect("failed to register global"); - - context.eval(src).expect("failed to execute script"); -} - -fn add_runtime(context: &mut Context<'_>) { - let console = Console::init(context); - context - .register_global_property(js_string!(Console::NAME), console, Attribute::all()) - .expect("the console builtin shouldn't exist"); -} diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx index 5c715047..96ee956c 100644 --- a/src-web/components/Sidebar.tsx +++ b/src-web/components/Sidebar.tsx @@ -165,7 +165,7 @@ export const Sidebar = memo(function Sidebar({ className }: Props) { tabIndex={hidden ? -1 : 0} className={classNames( className, - 'h-full pb-3 overflow-y-scroll overflow-x-visible pt-2', + 'h-full pb-3 overflow-y-scroll overflow-x-visible hide-scrollbars pt-2', )} >