Skip to content

rquickjs/rquickjs-extension

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rquickjs extension

github crates

This is a complement to rquickjs to allow the ecosystem to create more unified Rust extensions.

The goal was to create a more generic version of ModuleDef that would allow it to have options and/or set global values.

For example, a fetch extension could set a global fetch function and have as options an allowlist of domains.

Using extensions

If you are a consumer of extensions created using that crate, here is how you can import them in your runtime.

use rquickjs::AsyncRuntime;
use rquickjs_extension::ExtensionBuilder;

#[tokio::main]
async fn main() {
    let rt = AsyncRuntime::new().unwrap();

    let (loader, resolver, initalizer) = ExtensionBuilder::new().with_module(MyExtension).build();

    rt.set_loader(resolver, loader).await;

    let ctx = AsyncContext::full(&rt).await.unwrap();

    async_with!(ctx => |ctx| {
        if let Err(err) = initalizer.init(&ctx).catch(&ctx){
            eprintln!("{:?}", err);
        }
    })
    .await;
}

Creating extensions

For the base case, replace the ModuleDef by an implementation of Extension.

use rquickjs::{Ctx, JsLifetime, Object, Result};
use rquickjs_extension::{Extension, ModuleImpl};

#[derive(JsLifetime, Debug)]
struct MyExtensionOptions {
    user: String,
}

struct MyExtension {
    options: MyExtensionOptions,
}

impl MyExtension {
    pub fn new<T: Into<String>>(user: T) -> Self {
        Self {
            options: MyExtensionOptions {
                user: user.into(),
            },
        }
    }
}

impl Extension<MyExtensionOptions> for MyExtension {
    // Use `ModuleImpl` when you want a Javascript module.
    // The options generic is not required.
    type Implementation = ModuleImpl<MyExtensionOptions>;

    fn implementation() -> &'static Self::Implementation {
        // This is the same as the implementation of `ModuleDef`
        &ModuleImpl {
            declare: |decl| {
                decl.declare("user")?;
                Ok(())
            },
            evaluate: |ctx, exports, options| {
                exports.export("user", options.user.clone())?;
                Ok(())
            },
            name: "my-module",
        }
    }

    fn options(self) -> MyExtensionOptions {
        self.options
    }

    fn globals(globals: &Object<'_>, options: &MyExtensionOptions) -> Result<()> {
        // Set your globals here
        globals.set("global_user", options.user.clone())?;
        Ok(())
    }
}

At runtime, this extension results in:

  • A global variable named global_user
  • An importable module import { user } from "my-module"

Globals only

If you only need to set globals and do NOT want a Javascript module, use GlobalsOnly for the implementation.

use rquickjs::{Object, Result};
use rquickjs_extension::{Extension, GlobalsOnly};

struct MyExtension;

impl Extension for MyExtension {
    type Implementation = GlobalsOnly;

    fn implementation() -> &'static Self::Implementation {
        &GlobalsOnly
    }

    fn options(self) {}

    fn globals(globals: &Object<'_>, options: &()) -> Result<()> {
        // Set your globals here
        globals.set("hello", "world".to_string())?;
        Ok(())
    }
}

You can also use the macro for a simpler experience:

use rquickjs_module::globals_only;

struct MyExtension;
globals_only!(MyExtension, |globals| {
    // Set your globals here
    globals.set("hello", "world".to_string())?;
    Ok(())
});

At runtime, both result in a global variable named hello.