How to call javascript in rust using boa-engine and esbuild?

Folder

Folder for esbuild

tests/modules/package.js

Esbuild

esbuild_tests.js

import { build } from "esbuild";
import { polyfillNode } from "esbuild-plugin-polyfill-node";

build({
  entryPoints: ["package.js"],
  bundle: true,
  outfile: "tests/modules/package.js",
  format: "esm",
  // sourcemap: 'inline',
  minify: true,
  plugins: [polyfillNode()],
}).catch(() => process.exit(1));

Node packages

yarn add esbuild
yarn add esbuild-plugin-polyfill-node

package.json

"scripts": {
  "esbuild-test": "node esbuild_tests.js"
}

Folders for js

js-api/hello.ts

export function helloWorld(): String {
  return "Hello, world!";
}

package.js

import { helloWorld } from "./js-api/hello";

export { helloWorld };

Example

https://github.com/boa-dev/boa/blob/main/examples/src/bin/modules.rs

Imports

#![allow(unused)]
fn main() {
use std::env;
use std::path::PathBuf;
use std::{error::Error, path::Path, rc::Rc};

use boa_engine::{
    builtins::promise::PromiseState, js_string, module::SimpleModuleLoader, Context, JsError,
    JsNativeError, JsValue, Module, NativeFunction,
};
use boa_parser::Source;
}
#![allow(unused)]
fn main() {
 let root = env::current_dir().unwrap();
 println!("Project root: {:?}", root);

 // Build path to package.js
 let js_file_path: PathBuf = root.parent().unwrap().join("tests/modules/package.js");
 println!("JS file path: {:?}", js_file_path);

 let path: &Path = js_file_path.as_path();

 println!("{:?}", path);

 let source = Source::from_filepath(path).unwrap();

 let module_pathbuf = root.parent().unwrap().join("tests/modules");

 let module_path: &Path = module_pathbuf.as_path();

 println!("{:?}", module_path);

 // // println!("source: {:?}", source);
 let loader = Rc::new(SimpleModuleLoader::new(module_path).unwrap());
 // // Instantiate the execution context
 let context = &mut Context::builder()
     .module_loader(loader.clone())
     .build()
     .unwrap(); // // Add the runtime intrisics
 let module = Module::parse(source, None, context).unwrap();

 loader.insert(
     Path::new(module_path)
         .canonicalize()
         .unwrap()
         .join("main.mjs"),
     module.clone(),
 );
 let promise_result = module
     // Initial load that recursively loads the module's dependencies.
     // This returns a `JsPromise` that will be resolved when loading finishes,
     // which allows async loads and async fetches.
     .load(context)
     .then(
         Some(
             NativeFunction::from_copy_closure_with_captures(
                 |_, _, module, context| {
                     // After loading, link all modules by resolving the imports
                     // and exports on the full module graph, initializing module
                     // environments. This returns a plain `Err` since all modules
                     // must link at the same time.
                     module.link(context)?;
                     Ok(JsValue::undefined())
                 },
                 module.clone(),
             )
             .to_js_function(context.realm()),
         ),
         None,
         context,
     )
     .then(
         Some(
             NativeFunction::from_copy_closure_with_captures(
                 // Finally, evaluate the root module.
                 // This returns a `JsPromise` since a module could have
                 // top-level await statements, which defers module execution to the
                 // job queue.
                 |_, _, module, context| Ok(module.evaluate(context).into()),
                 module.clone(),
             )
             .to_js_function(context.realm()),
         ),
         None,
         context,
     );

 // Very important to push forward the job queue after queueing promises.
 context.run_jobs();

 match promise_result.state() {
     PromiseState::Pending => {
         println!("Module can't load");
     }
     PromiseState::Fulfilled(v) => {
         assert_eq!(v, JsValue::undefined());
     }
     PromiseState::Rejected(err) => {
         println!(
             "{:?}",
             JsError::from_opaque(err).try_native(context).unwrap()
         );
     }
 }

 // We can access the full namespace of the module with all its exports.
 let namespace = module.namespace(context);

 let hello_world_namespace = namespace.get(js_string!("helloWorld"), context).unwrap();
 let hello_world = hello_world_namespace
     .as_callable()
     .ok_or_else(|| JsNativeError::typ().with_message("mix export wasn't a function!"))
     .unwrap();
 let result = hello_world
     .call(&JsValue::undefined(), &[], context)
     .unwrap();

 println!("result = {}", result.display());

}
cargo test -- --nocapture