组件化-qjs
将 JavaScript 源代码转换为 WebAssembly 组件 使用 快速JS。 。 。 .
概览
componentize-qjs 使用 JavaScript 源文件并使用
怀特 定义,
并生成一个独立的WebAssembly组件,可以在任意
组件模型运行时间(例如:. 时间) (中文(简体) ).
盖下:
- 内嵌 快速JS 引擎(通过) 鲁克吉斯作为 Java 脚本 运行时间.
- 用途 机智开发 生成 WIT 将组件模型和JS引擎连接起来.
- 初创的JS状态 怀泽 因此,启动费用按 构建时间, 而非运行时间 .
- Prunes 明确标出一次性设置代码, 并带有Walrus和条纹 从快照组件调试/ 构建元数据 .
先决条件
锈 1.97 (中文(简体) ) 或以后根据Wasmtime( wasm32-wasip2 目标也
(c) 需要最近在isi-libc中为事先知情同意提供支持的工具链.
安装
Rust CLI (crates.io) (英语)
cargo install componentize-qjs-cli --locked
此安装 componentize-qjs 命令.
Rust CLI(来自来源)
cargo install --path . --locked
预建的 CLI 二进制
预建的CLI档案都附在其中 GitHub 发布 页:1 Linux, macOS, 和Windows (英语).
npm 软件包
npm install componentize-qjs
右侧预建的本地装订器通过
@andreiltd/componentize-qjs-binding-* 可选依赖关系.
如果您想要从源代码创建 :
cd npm && npm install && npm run build
快速启动
1. 定义WIT接口 (单位:千美元)hello.wit:
package test:hello;
world hello {
export greet: func(name: string) -> string;
}
2. 在 JavaScript 中执行 (单位:千美元)hello.js:
JavaScript源是ES模块. 直接导出 WIT 函数和接口 从模块.
export function greet(name) {
return `Hello, ${name}!`;
}
3. 构筑部分:
componentize-qjs --wit hello.wit --js hello.js -o hello.wasm
4. 运行:
wasmtime run --wasm component-model-async=y --invoke 'greet("World")' hello.wasm
# "Hello, World!"
包含组件- qjs 的内置运行时间包含组件- 模型
自动支持 。 过 --sync 嵌入内置的非同步运行时间,
生成在主机上运行的组件,而无需组件模型的Aync支持. A 类
自定义运行时间也可以提供 --runtime。 。 。 .
CLI 参考
componentize-qjs [OPTIONS] --wit <WIT> --js <JS>
| 旗帜 | 短数 | 说明 |
|---|---|---|
--wit <PATH> | -w | WIT 文件或目录的路径 |
--js <PATH> | -j | JavaScript 源文件的路径 |
--output <PATH> | -o | 输出路径( 默认 ): output.wasm(中文(简体) ) |
--module-root <PATH> | 根目录在 Wizer 解析 JavaScript 导入时仅曝光读取 | |
--world <NAME> | -n | WIT 定义多世界时的世界名 |
--stub-wasi | 将所有 WASI 导入替换为陷阱根 | |
--minify | -m | 在嵌入前先减少联合来文来源 |
--opt-size | 使用内置的快速JS运行时间优化大小 | |
--sync | 使用内置的非同步运行时间( 与 --opt-size 非同步选择大小运行时间) | |
--runtime <PATH> | 自定义可嵌入的快速JS运行时间 Wasm 模块 |
货物特性
| 特性 | 效果 |
|---|---|
component-model-async | (默认) 嵌入组件模型 async 运行时间为默认内置 。 非同步运行时间总是通过 : --sync。禁用只创建非同步运行时间的更小的二进制 |
opt-size | 在 CLI 或 npm API 没有提供运行时间选项时, 选择捆绑的选项大小运行时间 |
带有特性的构建 :
cargo build --release --features opt-size
使用导入
WIT的进口可以使用其完全合格的WIT作为ES模块的进口 接口名称 :
// imports.wit
package local:test;
interface math {
add: func(a: s32, b: s32) -> s32;
multiply: func(a: s32, b: s32) -> s32;
}
world imports {
import math;
export double-add: func(a: s32, b: s32) -> s32;
}
// imports.js
import math from "local:test/math";
export function doubleAdd(a, b) {
const sum = math.add(a, b);
return math.multiply(sum, 2);
}
输入文件导入的 JavaScript 模块在 Wizer 期间解决
初始化。 相对导入从传递到的条目文件路径解析
--js;光包导入在只读模块根下解决. 以
默认 CLI 在条目文件下方使用当前目录,或
条目文件的父目录。 使用 --module-root <PATH> 将曝光
包含共享文件的工程根或 node_modules。 。 。 .
WIT 类型绘图
初级类型
| WIT 类型 | 联合来文类型 | 页:1 |
|---|---|---|
bool | boolean | |
u8, (中文(简体) ), u16, (中文(简体) ), u32 | number | |
s8, (中文(简体) ), s16, (中文(简体) ), s32 | number | |
u64, (中文(简体) ), s64 | BigInt | |
f32, (中文(简体) ), f64 | number | |
char | string | 一定是一个Unicode 平面值 |
string | string |
化合物类型
| WIT 类型 | 联合来文类型 | 示例 |
|---|---|---|
list<T> | Array | [1, 2, 3] |
list<u8> | Uint8Array 或者说 Array | new Uint8Array([1, 2, 3]) |
map<K, V> | Map | new Map([["key", value]]) |
tuple<T, U, ...> | Array | [42, "hello"] |
option<T> | T | undefined (包装: { tag: "some"|"none", val }(中文(简体) ) | undefined 无(null 当传递给东道主时,也接受为无 T 它本身是一个空洞的选择,如jco: option<option<T>> 包起来, option<option<option<T>>> 没有 |
result<T, E> | 顶级函数结果: 返回 T 或抛出 E; 嵌入结果 : { tag: "ok"|"err", val?: T|E } | return 42 编号 : throw "error" |
record { ... } | object (骆驼案件密钥) | { myField: 1 } |
variant | { tag: string, val?: T } | { tag: "circle", val: 2.5 } |
enum | string (案件名称) | "red" |
flags | object (骆驼Case布尔斯) | { read: true, write: false } |
own<R>, (中文(简体) ), borrow<R> | 资源对象(原型上的方法) | input.blockingRead(n) |
已导入资源
被导入的资源作为JavaScript类被曝光. 资源方法 调用手柄 :
import stdin from "wasi:cli/[email protected]";
import stdout from "wasi:cli/[email protected]";
const input = stdin.getStdin(); // an InputStream
const output = stdout.getStdout(); // an OutputStream
// Methods whose WIT return type is result<...> return the ok payload or throw.
const chunk = input.blockingRead(4096); // method on the resource (len is a number)
output.blockingWriteAndFlush(chunk);
[static] 方法在资源类上被曝光 [constructor] 制造
可调用的类 new。 。 。 .
拥有进口资源支助 [Symbol.dispose]() 确定性
清净相. 传递一个给WIT own<T> 参数转让所有权和
取消原包装。 借入的包装纸在部件到期后失效
呼叫完成,不能作为所有者转移或处置。 借出的资源
飞行中进口物品仍然活着,直到
进口完成。 对未完成的流/未来写作恢复所有权
并且对于在开始之前取消的输入电话。 使用
在从出口返回前输入所借资源.
取消 Async 导入
即将进口的合成显示 cancel() 遵守诺言 呼唤它
请求取消该导入。 如果取消成功, 则使用原版
答应拒绝 async import cancelled; 首先完成的电话可能
仍然正常地解决。 所附出口仍然活跃,因此其渔获量/最后
块可以等待更多的主机操作.
等待最初的承诺,然后释放它借来的资源.
打电话 cancel() 从开始进口的同一种合成导出引用;
它不产生任何其他出口援引的效果.
立即完成的电话可能没有 cancel 方法; 使用可选
完成时间未知时的链式。 重复取消是无害的,
并保存的取消功能不能影响复用
同样的运行时柄.
const pending = service.compute(input);
const abort = () => pending.cancel?.();
signal.addEventListener("abort", abort);
try {
return await pending;
} finally {
signal.removeEventListener("abort", abort);
await service.release();
}
Async 导出
Async出口申报与 async wIT中的关键词已执行
作为 Java 脚本 async 函数:
package example:greeting;
world greeter {
export greet: async func(name: string) -> string;
}
export async function greet(name) {
// You can use await here
await Promise.resolve();
return `Hello, ${name}!`;
}
输出出的资源方法和静态方法也可能是同化. 执行
作为 async 在导出 JavaScript 类上的成员; 资源构建器
保持同步 :
package example:counter;
interface counter-api {
resource counter {
constructor(initial: u32);
add: async func(value: u32) -> u32;
create: static async func(initial: u32) -> counter;
}
}
world counter {
export counter-api;
}
class Counter {
constructor(initial) { this.value = initial; }
async add(value) { this.value += value; return this.value; }
static async create(initial) { return new this(initial); }
}
export const counterApi = { Counter };
流线
串流在组件之间传输一系列值。 提升WIT流 执行 JavaScript 可同步插入的协议,以及同步或同步 可操作性自动降低到WIT流. 推断出流类型 从函数参数或结果中:
package example:streaming;
world streaming {
export uppercase: async func(input: stream<string>) -> stream<string>;
}
export async function uppercase(input) {
return (async function* () {
for await (const value of input) {
yield value.toUpperCase();
}
})();
}
除 stream<u8>,哪个
限值 Uint8Array 块来避免一个字节的承诺.
使用 wit.Stream 当需要直接进入两个终点时。 若为
wIT世界中只有一个流类型,该类型可能被省略:
const { readable, writable } = wit.Stream(wit.Stream.U8);
await writable.writeAll(new Uint8Array([1, 2, 3]));
writable.drop();
wit.Stream.from 明确调整可执行性并显示完成性
承诺:
const source = (async function* () {
yield "one";
yield "two";
})();
const { readable, completion } = wit.Stream.from(source, wit.Stream.STRING);
可用类型常数(来自 WIT 元数据):
| WIT 类型 | 常数 |
|---|---|
stream<u8> 编号 : future<u8> | wit.Stream.U8 编号 : wit.Future.U8 |
stream<u32> 编号 : future<u32> | wit.Stream.U32 编号 : wit.Future.U32 |
stream<string> 编号 : future<string> | wit.Stream.STRING 编号 : wit.Future.STRING |
stream<f64> 编号 : future<f64> | wit.Stream.F64 编号 : wit.Future.F64 |
所有建筑师返回 { readable, writable }。 。 。 .
复杂元素类型 也得到支持。 类型常数生成 从 WIT 类型结构递归:
// stream<result<string, u32>>
wit.Stream(wit.Stream.RESULT_STRING_U32);
// stream<option<u32>>
wit.Stream(wit.Stream.OPTION_U32);
// stream<tuple<u32, string>>
wit.Stream(wit.Stream.TUPLE_U32_STRING);
// Named record types use their WIT name:
// record point { x: f64, y: f64 }
// stream<point>
wit.Stream(wit.Stream.POINT);
WIT流的明确别名也作为常数被曝光:
type prompt-stream = stream<message-chunk>;
wit.Stream(wit.Stream.PROMPT_STREAM);
使用 wit.Stream.types 或者说 wit.Future.types 以发现所有可用的类型
运行时常数.
可流读方法 :
| 方法 | 回返 | 说明 |
|---|---|---|
read(count?) | Promise<T[]> (或 Uint8Array 页:1 u8(中文(简体) ) | 读取最多 count 数值 |
next() | Promise<IteratorResult<T>> | 读取下一个值( E)Uint8Array 块用于 u8(中文(简体) ) |
return() | Promise<IteratorResult<T>> | 结束重复并释放可读手柄 |
cancelRead() | 结果或 undefined | 取消一个在建工程 |
drop() | void | 释放流柄 |
[Symbol.asyncIterator]() | StreamReadable | 控制溪流 for await...of |
可简化的方法 :
| 方法 | 回返 | 说明 |
|---|---|---|
write(data) | Promise<number> | 写入值, 返回数已写入 |
writeOne(value) | Promise<number> | 精确写入一个值, 包括阵列形状的 WIT 值 |
writeAll(data) | Promise<number> | 写入所有值, 按需要重试 |
writeIterableItem(value) | Promise<boolean> | 写入一个可重复的产量, 批量匹配数组 |
cancelWrite() | 结果或 undefined | 取消正在写 |
drop() | void | 释放流柄 |
未来
期货转让单值. 他们的工作就像溪流,但背着一个 值 :
package example:async-value;
world async-value {
export compute: async func() -> future<string>;
}
export async function compute() {
const { readable, writable } = wit.Future();
// Write the value (fire-and-forget; completes when reader reads)
writable.write("computed result");
return readable;
}
未来类型常数 遵循相同的模式: wit.Future.U32, (中文(简体) ),
wit.Future.STRING等 (中文(简体) ).
将来的手柄是故意的,不能用到的 await readable.read() 改为
获取有效载荷。 一直这样 return readable 在从
含蓄地消耗着未来, 并保留了巢中的未来柄.
重复 read() 调用相同的承诺; 取消的读取拒绝
承诺并允许后续读取重试.
wit.Future.from(value, type) 调整一个值或承诺并返回
{ readable, completion }返回 readable 取自同步导出
而不是直接返回有效载荷, JavaScript 将等待.
可读方法:
| 方法 | 回返 | 说明 |
|---|---|---|
read() | Promise<T> | 读取单值 |
cancelRead() | 结果或 undefined | 取消一个在建工程 |
drop() | void | 释放未来的手柄 |
可写明的方法 :
| 方法 | 回返 | 说明 |
|---|---|---|
write(value) | Promise<boolean> | 写入值, 返回成功 |
cancelWrite() | 结果或 undefined | 取消正在写 |
drop() | void | 释放未来的手柄 |
资源清理
拥有进口资源、流程终点和未来终点支持
明确资源管理
通过 Symbol.dispose在支持环境 using数字 :
{
const stream = wit.Stream();
using writable = stream.writable;
using readable = stream.readable;
// Each endpoint is disposed when leaving scope.
}
工厂的 { readable, writable } 配对本身不是一次性的.
完成一个端点的待办业务(或取消并等待)
和解问题)之前 using 范围或呼叫 .drop()。 。 。 .
主机请求的任务取消保留待ABI缓冲直到主机
确认每个操作的完成或取消.
否则,打电话 .drop() 明确释放手柄.
快照初始化
快照创建使用 1 个 GiB 主机调用复制预算来容纳大型模块 图表。 此津贴属于构建时间初始化器; 执行主机 继续为即时组件选择自己的预算.
节点.js API
Npm软件包既暴露了CLI,也暴露了方案API.
国 际
npx componentize-qjs --wit hello.wit --js hello.js -o hello.wasm
(或者,如果你在全球安装软件包 componentize-qjs .... ) (中文(简体) )
使用量
import { componentize } from "componentize-qjs";
const { component } = await componentize({
witPath: "hello.wit",
jsSource: "export function greet(name) { return `Hello, ${name}!`; }",
optSize: true,
});
// component is a Buffer containing the WebAssembly component bytes
运行时间选择通过 optSize, (中文(简体) ), sync, (中文(简体) ), runtime,或
runtimeBytes。 。 。 . optSize 财务报告和审定财务报表 sync 可合并选择非同步
选择大小的运行时间, 但两者都不能与自定义合并 runtime编号 :runtimeBytes。 。 。 .
这个 runtime 选项是自定义的快速JS运行时间Wasm模块的路径.
承认
这个项目以下列观点和守则为基础:
- 构成部分化JS 由乔尔·迪斯所著
- lua- 组件- 演示 亚历克斯·克赖顿
许可证
许可证根据 阿帕奇 - 2.0。 。 。 .