27 个版本 (7 个破坏性更新)
0.8.3 | 2024 年 8 月 8 日 |
---|---|
0.8.0 | 2024 年 7 月 29 日 |
0.3.0 | 2024 年 1 月 2 日 |
0.2.1 | 2023 年 11 月 13 日 |
#140 in 网页编程
每月 1,266 次下载
在 ejs 中使用
345KB
5.5K SLoC
为 Rust 提供轻松的 JS 集成
此 crate 旨在提供一种快速简单的方式,在 Rust 中集成运行时 JavaScript 或 TypeScript 组件。
它通过 deno_core
crate 使用 v8 引擎,旨在尽可能简单易用,同时不牺牲灵活性和性能。
我还尝试抽象化 v8 引擎的细节,以便您大部分情况下可以直接在 Rust 类型上操作。
- 默认情况下,正在运行的代码完全隔离于主机,没有文件系统或网络访问。
- 如果需要,可以扩展以包括这些功能和其他功能 - 请参阅
web
特性和runtime_extensions
示例
- 如果需要,可以扩展以包括这些功能和其他功能 - 请参阅
- 支持异步 JS 代码(建议在创建运行时时使用超时选项)
- 加载的 JS 模块可以导入其他模块
- 默认支持 TypeScript,并会在执行时将其转换为 JS
以下是如何使用此 crate 执行 JS 模块的一个非常基本的例子。它会
- 创建一个基本的运行时
- 加载一个 JavaScript 模块
- 调用作为入口点的注册函数
- 返回结果值
use rustyscript::{json_args, Runtime, Module, Error};
let module = Module::new(
"test.js",
"
export default (string, integer) => {
console.log(`Hello world: string=${string}, integer=${integer}`);
return 2;
}
"
);
let value: usize = Runtime::execute_module(
&module, vec![],
Default::default(),
json_args!("test", 5)
)?;
assert_eq!(value, 2);
还可以使用 Module::load
或 Module::load_dir
从文件系统中加载模块,如果您想收集给定目录中的所有模块。
如果您只需要单个 JavaScript 表达式的结果,可以使用
let result: i64 = rustyscript::evaluate("5 + 5").expect("The expression was invalid!");
或者仅导入单个模块以供使用
use rustyscript::{json_args, import};
let mut module = import("js/my_module.js").expect("Something went wrong!");
let value: String = module.call("exported_function_name", json_args!()).expect("Could not get a value!");
还包括一些其他实用工具,如 validate
和 resolve_path
以下是一个更详细的 crate 使用说明,它分解了步骤而不是使用一行代码 Runtime::execute_module
use rustyscript::{json_args, Runtime, RuntimeOptions, Module, Error, Undefined};
use std::time::Duration;
let module = Module::new(
"test.js",
"
let internalValue = 0;
export const load = (value) => internalValue = value;
export const getValue = () => internalValue;
"
);
// Create a new runtime
let mut runtime = Runtime::new(RuntimeOptions {
timeout: Duration::from_millis(50), // Stop execution by force after 50ms
default_entrypoint: Some("load".to_string()), // Run this as the entrypoint function if none is registered
..Default::default()
})?;
// The handle returned is used to get exported functions and values from that module.
// We then call the entrypoint function, but do not need a return value.
//Load can be called multiple times, and modules can import other loaded modules
// Using `import './filename.js'`
let module_handle = runtime.load_module(&module)?;
runtime.call_entrypoint::<Undefined>(&module_handle, json_args!(2))?;
// Functions don't need to be the entrypoint to be callable!
let internal_value: i64 = runtime.call_function(Some(&module_handle), "getValue", json_args!())?;
大多数运行时函数也有 '_async' 和 'immediate' 版本;'_async' 函数返回一个解析到操作结果的 future,而 '_immediate' 函数将不会尝试等待事件循环,因此它们适合用于 crate::js_value::Promise
Rust 函数也可以注册为从 JavaScript 调用
use rustyscript::{ Runtime, Module, serde_json::Value };
let module = Module::new("test.js", " rustyscript.functions.foo(); ");
let mut runtime = Runtime::new(Default::default())?;
runtime.register_function("foo", |args| {
if let Some(value) = args.get(0) {
println!("called with: {}", value);
}
Ok(Value::Null)
})?;
runtime.load_module(&module)?;
异步 JS 可以通过两种方式调用;
第一种是使用 JS 中的 'async' 关键字,然后使用 Runtime::call_function_async
调用函数
use rustyscript::{ Runtime, Module, json_args };
let module = Module::new("test.js", "export async function foo() { return 5; }");
let mut runtime = Runtime::new(Default::default())?;
// The runtime has its own tokio runtime; you can get a handle to it with [Runtime::tokio_runtime]
// You can also build the runtime with your own tokio runtime, see [Runtime::with_tokio_runtime]
let tokio_runtime = runtime.tokio_runtime();
let result: i32 = tokio_runtime.block_on(async {
// Top-level await is supported - we can load modules asynchronously
let handle = runtime.load_module_async(&module).await?;
// Call the function asynchronously
runtime.call_function_async(Some(&handle), "foo", json_args!()).await
})?;
assert_eq!(result, 5);
第二种是使用 crate::js_value::Promise
use rustyscript::{ Runtime, Module, js_value::Promise, json_args };
let module = Module::new("test.js", "export async function foo() { return 5; }");
let mut runtime = Runtime::new(Default::default())?;
let handle = runtime.load_module(&module)?;
// We call the function without waiting for the event loop to run, or for the promise to resolve
// This way we can store it and wait for it later, without blocking the event loop or borrowing the runtime
let result: Promise<i32> = runtime.call_function_immediate(Some(&handle), "foo", json_args!())?;
// We can then wait for the promise to resolve
// We can do so asynchronously, using [crate::js_value::Promise::into_future]
// But we can also block the current thread:
let result = result.into_value(&mut runtime)?;
assert_eq!(result, 5);
- 有关在 JS 中注册和调用异步 Rust 的信息,请参阅
Runtime::register_async_function
- 有关使用异步 JS 的更详细示例,请参阅
examples/async_javascript.rs
为了提高调用 Rust 代码的性能,考虑使用扩展而不是模块 - 请参阅 runtime_extensions
示例以获取详细信息
可以使用线程化的工作线程来在单独的线程中运行代码,或者允许多个并发运行时。
worker
模块提供了一个简单的接口来创建和交互工作线程。可以实现 worker::InnerWorker
trait 来提供自定义工作线程行为。
它还提供了一个默认的工作线程实现,无需任何额外的设置即可使用。
use rustyscript::{Error, worker::{Worker, DefaultWorker, DefaultWorkerOptions}};
use std::time::Duration;
fn main() -> Result<(), Error> {
let worker = DefaultWorker::new(DefaultWorkerOptions {
default_entrypoint: None,
timeout: Duration::from_secs(5),
})?;
let result: i32 = worker.eval("5 + 5".to_string())?;
assert_eq!(result, 10);
Ok(())
}
实用函数
这些函数提供了一种简单的一行代码访问此 crate 常见功能的方法
evaluate
;评估单个 JS 表达式并返回结果值import
;获取一个 JS 模块的句柄,从中可以获取导出的值和函数resolve_path
;将相对路径解析为当前工作目录validate
;验证 JS 表达式的语法init_platform
;为多线程应用程序初始化 V8 平台
Crate 功能
下表列出了此 crate 可用的功能。标记为 Preserves Sandbox: NO
的功能会破坏加载的 JS 模块和宿主系统之间的隔离。请谨慎使用。
有关功能的更多详细信息,请参阅 Cargo.toml
请注意,web
功能还会启用 fs_import
和 url_import
,允许对导入语句进行任意文件系统和网络访问
- 这是因为
deno_web
crate 已经允许进行 fetch 和 FS 读取
功能 | 描述 | Preserves Sandbox | 依赖项 |
---|---|---|---|
cache |
实现了 Deno 的 Cache API | NO | deno_cache ,deno_webidl ,deno_web ,deno_crypto ,deno_fetch ,deno_url ,deno_net |
控制台 |
提供来自JS的console.* 功能 |
是 | deno_console |
加密 |
提供来自JS的crypto.* 功能 |
是 | deno_crypto ,deno_webidl |
URL |
从JS内部提供URL 和URLPattern API |
是 | deno_webidl ,deno_url |
输入/输出 |
提供诸如stdio流和文件系统文件抽象等IO原语 | NO | deno_io ,rustyline ,winapi ,nix ,libc ,once_cell |
Web |
从JS内部提供Event ,TextEncoder ,TextDecoder ,File ,Web Cryptography和fetch API |
NO | deno_webidl ,deno_web ,deno_crypto ,deno_fetch ,deno_url ,deno_net |
Web存储 |
提供WebStorage API |
NO | deno_webidl ,deno_webstorage |
WebSocket |
提供WebSocket API |
NO | deno_web ,deno_websocket |
WebIDL |
提供webidl API |
是 | deno_webidl |
默认 |
仅提供那些保留沙盒的扩展 | 是 | deno_console ,deno_crypto ,deno_webidl ,deno_url |
无扩展 |
禁用所有JS运行时的扩展 - 您仍然可以在这种模式下添加自己的扩展 | 是 | 无 |
全部 |
提供所有可用功能 | NO | deno_console ,deno_webidl ,deno_web ,deno_net ,deno_crypto ,deno_fetch ,deno_url |
fs_import |
启用通过JS从文件系统导入任意代码 | NO | 无 |
url_import |
启用通过JS从网络位置导入任意代码 | NO | reqwest |
worker |
启用对线程化worker API的访问 worker |
是 | 无 |
snapshot_builder |
启用对SnapshotBuilder 的访问,这是一个用于创建可提高启动时间的快照的运行时 |
是 | 无 |
web_stub |
启用一组不会破坏沙盒的web 功能 |
是 | deno_webidl |
有关此crate的使用示例,请参阅Lavendeux
请参阅@Bromeon/js_sandbox,这是该领域另一个优秀的crate
依赖项
~107MB
~2M SLoC