DelSkayn / rquickjs

High level bindings to the quickjs javascript engine
MIT License
434 stars 59 forks source link

Unable to use import syntax #274

Closed Mnib closed 3 months ago

Mnib commented 4 months ago

Hi, I am currently working on a Rust project that needs to run JS scripts. However I am unable to load other JS modules.

Here is a minimal example:

use rquickjs::{
    loader::{FileResolver, ModuleLoader, ScriptLoader},
    prelude::Func,
    promise::Promised,
    Function,
};

fn print(msg: String) {
    println!("{msg}");
}

async fn delay(msec: u32) {
    let duration = std::time::Duration::from_millis(msec as _);
    tokio::time::sleep(duration).await;
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let resolver = (FileResolver::default()
        .with_path("./")
        .with_pattern("{}.js")
        .with_native(),);

    let loader = (ModuleLoader::default(), ScriptLoader::default());

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

    rt.set_loader(resolver, loader).await;

    let code = r#"
        import { a } from './a.js';

        async function test() {
            print('Hello');

            await delay(1000);

            print('Hello again');

            const a_result = a();
            print(a_result);

            print("Bye");
        }

        test();
    "#;

    ctx.with(move |ctx| {
        let global = ctx.globals();

        global
            .set(
                "print",
                Function::new(ctx.clone(), print)
                    .unwrap()
                    .with_name("print")
                    .unwrap(),
            )
            .unwrap();

        global
            .set("delay", Func::from(|msec| Promised(delay(msec))))
            .unwrap();

        if let Err(e) = ctx.eval::<(), _>(code) {
            println!("eval: {:#?}", e);
        }
    })
    .await;

    rt.idle().await;
    println!("Idle");

    Ok(())
}

a.js looks like this:

export function a() {
    return 'Result of the a() function';
}

Here are the dependencies and version used:

[dependencies]
rquickjs = { version = "0.5.1", features = ['loader', 'futures', 'bindgen'] }
tokio = { version = "1.36.0", features = ['full'] }

What is happening:

ctx.eval fails and returns Exception generated by QuickJS.

What is expected:

Module loading normally

Workarount:

Using await import(<FILE_PATH>); works well.

Am I missing something?

Thanks in advance

DelSkayn commented 4 months ago

Regarding the error you received Exception generated by QuickJS that is a stub error. QuickJS actually throws a more detailed error but you need to retrieve the details from the context. This can be done with CatchResultExt::catch. This will give you a more detailed error message with what exactly is going wrong, and might also give me a better indication of what is going wrong.

Mnib commented 3 months ago

Hello,

The error returned by catch is the following:

Error:[eval_script]:2:15 expecting '('
    at eval_script:2:15

So it looks like the error lies here:

import { a } from './a.js';
      ^

I guess this would mean that the

import something from 'some-file.js';

syntax is not supported.

The supported syntax would be:

const { a } = await import('./a.js');

Am I Correct?

Thanks

DelSkayn commented 3 months ago

That is partially correct, import { ... } from 'foo' is only supported in modules not in scripts. Ctx::eval runs the javascript as a script. The import syntax should work if you instead use Module::evaluate to execute the js as a module.

Mnib commented 3 months ago

Understood!

Thanks for the help, I am closing this issue