Gox

module
v0.15.24 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: MIT

README

Gox

用 Go 从零实现的 JavaScript 运行时:词法分析 → 语法分析 → 字节码编译 → 栈式虚拟机执行。 单二进制、零 cgo、零外部运行时依赖,自带软件光栅化 GUI 与脚本打包器。

Go License npm Platform Release test262 language test262 built-ins

📖 官网与使用教程 | GUI 开发指南 | npm 包

English version: README.en.md。两份 README 同步维护,描述同一个构建。


目录


概览

Gox 是一个用纯 Go 实现的 JavaScript(ES6+ 子集)运行时。整条编译管线 —— 词法分析、语法分析、 字节码生成、栈式虚拟机 —— 都从零实现,不依赖 V8/QuickJS 等任何现成引擎,也不依赖 cgo。

在此之上,Gox 补齐了脚本语言通常缺失的那一层:一套自研的桌面 GUI 渲染层(纯 Go 软件光栅化, flex 布局 + JSX + 信号驱动更新,win32 / X11 / cocoa 窗口后端)、一组宿主能力模块(文件、HTTP、剪贴板、 原生对话框、持久化存储),以及把脚本打包成独立可执行文件的工具链。

它的定位是"小而完整":一个 go build 得到一个可执行文件,可以把 JS 当脚本层嵌进 Go 程序, 也可以直接用它写带界面的小工具并打包分发。

设计取向
取向 做法 代价
单二进制分发 静态编译、无动态库依赖;跨平台只需换 GOOS/GOARCH,无需目标机工具链 二进制体积较大
零 cgo 后端直连系统 API(Windows 走 syscall、Linux 走 X 协议、macOS 走 cocoa/purego) 不支持 -race 检测
GUI 不是外挂 渲染、布局、命中测试、脏矩形重绘全在运行时内部,脚本侧只有 JSX 与信号 渲染能力受软件光栅化上限约束
确定性单线程 VM 单线程执行,网络 I/O 在 goroutine 中完成后投递回主线程,回调无需加锁 无真正的并行执行
缺能力明说缺 宿主能力走 canIUse() + 8 个统一错误码,不返回假数据、不假装在播 某些场景下比"尽力而为"更早暴露问题
Gox 不适合

把边界写在前面,比让读者踩坑再回头更省事:

  • 不适合做 Web 后端或替代 Node.js —— 不支持 CommonJS(require)与 npm 生态的大多数包,见 兼容性矩阵。
  • 不适合 CPU 密集的计算任务 —— 纯解释器,无 JIT,fib28 比 goja 慢约 3 倍、比 V8 慢一个数量级,见 性能。
  • 不适合需要完整 ES 规范的生产系统 —— test262 built-ins 合规率 62%,Temporal / RegExp 等子集仍有成块缺口。
  • 不适合"一次写完到处跑"的幻想 —— 三端形态(折叠屏、安全区、权限)差异真实存在,需要按平台适配。

适用场景:用 JS 写跨平台桌面小工具;把 JS 作为配置与插件层嵌入 Go 应用;学习编译器与虚拟机实现。


项目状态

Gox 处于 0.x 活跃开发期 —— 公共 API 可能在 minor 版本间发生破坏性变更(0.x 下 minor 即破坏位)。

test262 合规率

官方 ECMAScript 一致性套件,由 CI 每周定时跑批并自动回写徽章(docs/test262-*.json):

套件 合规率 口径
language 97.0% 语言语义(语句 / 表达式 / 模块 / 函数 / class 等)
built-ins 62.1% 内置对象(Array / String / Temporal / TypedArray …),14804 / 23823

两个套件分母不同、构成完全不同,所以各守一条独立的下限基线;intl402(需 i18n)Gox 未实现,不在 runner 的套件内。 合规率是趋势观测而非验收门禁 —— 失败面按「目录族 × 失败形态」聚类成 502 个簇,排期按簇开单。 完整口径与跑法见 官网 · 性能与合规。

平台支持矩阵
平台 运行时 GUI 窗口后端 CI 覆盖
Windows x64 ✅ win32(纯 syscall) ci.yml 构建 + 测试
Linux x64 / arm64 ✅ X11(纯协议) ci.yml 构建 + 测试
macOS x64 / arm64 ✅ cocoa(purego) ci.yml 构建 + 测试
Android arm64 ✅ 预编译库 壳工程(Kotlin 宿主) mobile-smoke.yml + 折叠屏模拟器回归
iOS arm64 ✅ 预编译库 壳工程(Swift 宿主) mobile-smoke.yml 模拟器三机型;真机验收待做
鸿蒙 arm64 ⚠️ 交叉编译通过 壳工程(ArkTS),六个能力模块仍是桩 构建通过;HAP 签名是硬阻塞,模拟器验收待做
已知缺口
  • 富文本(行内混排样式)尚未封装;虚拟化长列表已由 <scroll vlist> 落地
  • 原生能力层中依赖真机的部分(相机 / 定位 / 相册 / 权限)桌面端诚实报 unsupported
  • CommonJS / require 明确不做;npm 生态仅 uuid 等极少数库部分可用
  • 逐项见 docs/gui-guide.md 与官网 限制与常见误区

快速开始

环境要求
场景 要求
从源码构建 Go 1.26.2 或更高(零 cgo,无需 C 工具链)
仅运行脚本 无 —— npm 包已内置各平台预编译二进制,只需 Node.js ≥ 14
GUI 应用 Windows(win32)、Linux(X11)或 macOS(cocoa)
三种装入方式
# 1) npm —— 最省事,五平台预编译二进制随包分发
npm i -g @goxjs/goxjs    # 安装后可直接使用 goxjs 命令
npx goxjs app.js         # 或不安装,直接运行

# 2) 源码构建 —— 需要一个 Go 工具链
git clone https://github.com/14752222/Gox.git
cd Gox
go build ./cmd/gox       # 生成可执行文件;Windows 下为 gox.exe

# 3) go install —— 直接装进 GOBIN(需先发布过 tag)
go install github.com/14752222/Gox/cmd/gox@latest

npm/ · website/ · gox-logo-concepts/ 是三个独立仓库(gox-npm · gox-website · gox-logo-concepts),2026-10-12 起不再以 git submodule 挂载,克隆主仓不需要 --recurse-submodules。CI 构建时会自动 checkout npm/ 与 website/;本地要跑 scripts/check-registries.py 的话,先 git clone https://github.com/14752222/gox-npm npm(否则读不到 npm/package.json)。

交互式 REPL
./gox                # Unix;Windows 下为 gox.exe
Gox REPL (ES6 subset)
Type :exit to quit, :help for help

> let x = 10
> let y = 20
> x + y
  30
> [1, 2, 3].map(v => v * 2)
  [2, 4, 6]

REPL 命令::help 查看帮助、:clear 重置环境、:exit 退出。

运行脚本
./gox example.js    # npm 安装的 goxjs 命令用法相同

脚本执行完毕后会回显最后一个顶层表达式的值(undefined 除外),并等待定时器与异步回调 全部执行完再退出。


首屏示例:单双栏自适应列表页

窗口宽度 < 600dp 时单栏,拖宽到 ≥ 600dp 自动变双栏(左侧列表 + 右侧详情)。 拖拽窗口边缘即触发,不用监听 resize,也不用轮询尺寸。完整可跑文件: testdata/adaptive_list_demo.js(gox testdata/adaptive_list_demo.js)。

import { h, render } from "gx/gfx";
import { createSignal } from "gx/solid";
import { useBreakpoint, matchBreakpoint } from "gx/viewport";
import { Switch, Match } from "gx/view";

const bp = useBreakpoint();                 // () => "sm" | "md" | "lg" | "xl"(订阅型)
const [selected, setSelected] = createSignal(0);

const ITEMS = [
  { id: "a", title: "编译管线", desc: "词法 → 语法 → 字节码 → 栈式 VM,160 个操作码。" },
  { id: "b", title: "软件光栅化 GUI", desc: "flex 布局 + JSX + 信号驱动,三后端。" },
  { id: "c", title: "脚本打包器", desc: "把 .js 打成单个可执行文件。" },
];

// 布局分支由断点名声明(一张 sm/md/lg/xl 的表),
// 而不是把同一个宽度判断散落在 N 处 if 里 —— 加一档只改表,不改判断。
const layout = () => {
  bp();   // ← 无条件读一次 = 订阅登记。删掉这行,整页不再自适应(无警告)
  return matchBreakpoint({
    sm: { cols: 1, side: false },   // <600dp  单栏
    md: { cols: 2, side: true },    // >=600dp 双栏
    lg: { cols: 2, side: true },
    xl: { cols: 2, side: true },
  });
};

const List = () => (
  <column gap={6}>
    <view each={ITEMS} key="id">
      {(it, i) => (
        <row gap={6} padding={8} radius={6}
             background={selected() === i ? "#dce8ff" : "#f2f5fa"}
             onClick={() => setSelected(i)}>
          <text font={13} color="#334">{it.title}</text>
        </row>
      )}
    </view>
  </column>
);

const Detail = () => (
  <column gap={6} padding={12} radius={8} background="#ffffff">
    <text font={14} color="#223">{ITEMS[selected()].title}</text>
    <text font={12} color="#667">{ITEMS[selected()].desc}</text>
  </column>
);

render(
  <window title="自适应列表页" width={520} height={420}>
    <column gap={10} padding={14}>
      <text font={15}>{() => `breakpoint = ${bp()}   →   ${layout().side ? "双栏" : "单栏"}`}</text>
      <Switch>
        <Match when={() => layout().side}>
          <row gap={10}>
            <column width={210} gap={6}><List /></column>
            <column flex={1}><Detail /></column>
          </row>
        </Match>
        <Match when={() => true}>
          <column gap={8}>
            <List />
            <Detail />
          </column>
        </Match>
      </Switch>
    </column>
  </window>
);

两个最容易踩的坑(示例注释里同样写明):

  1. 纯读数会短路掉订阅 —— breakpoint() / posture() / hasFold() 是纯读数、不订阅;只有 useBreakpoint() 这类两段式返回的取值函数才订阅。把纯读数写在三元的第一操作数或 && 左侧, 被短路的那支一次都没读 ⇒ 零依赖 ⇒ 之后宽度变了这一支永不重跑,没有任何警告。 gfx/adaptive_list_test.go 把这条钉成了断言:纯读数快照拖宽后仍停在首帧的值。
  2. 坐标单位是两套 —— reservedRegions() 的 x/y/width/height 是设备像素(折痕、挖孔), safeAreaStyle() 与脚本显式写的尺寸属性是 dp(内核每帧乘 Display.Scale)。 折痕避让时不要把前者直接喂给布局属性,先除 pixelRatio。

能力总览

能力域 内容 文档
语言运行时 160 个操作码、定长 3 字节指令;ES6+ 语言子集;完整内置对象族 语言与内置对象
GUI 渲染层 63 个内置元素、flex 布局、JSX、脏矩形局部重绘、win32 / X11 / cocoa 三后端 docs/gui-guide.md
宿主能力 全局注入 fs / path / http / fetch / process / stats / crypto / hash;gx/* 内置模块 23 个 宿主能力模块
移动端与形态 Android / iOS / 鸿蒙壳工程 + gx/screen 折叠屏与安全区语义 docs/foldable-adaptation.md
工具链 REPL、gox create 脚手架、gox dev 热更新、gox cert / gox build / gox sync / gox icon、jsbuild 打包 打包与工具链

语言与内置对象

语法子集

let/const/var、函数与箭头函数、闭包、class(含私有字段 #name)、async/await、 解构赋值、剩余/默认参数、展开、模板字符串、for...of、for...in、try/catch/throw、 可选链 ?.、空值合并 ??、ES 模块 import/export,以及 JSX 语法 (编译期降级为 h(tag, props, ...children) 调用)。

let x = 10
let y = 20
console.log(x + y)                                                 // 30
console.log([1, 2, 3].map(v => v * 2))                             // [2, 4, 6]
console.log([1, 2, 3].filter(v => v > 1).reduce((a, b) => a + b))  // 5
console.log(Math.hypot(3, 4))                                      // 5

let name = "Gox"
console.log(`Hello, ${name}!`)                                     // Hello, Gox!

let [a, b] = [1, 2]                                                // 解构赋值
console.log(a + b)                                                 // 3
class Point {
  constructor(x, y) { this.x = x; this.y = y }
  norm() { return Math.hypot(this.x, this.y) }
}
console.log(new Point(3, 4).norm())       // 5

async function main() {
  let v = await delay(10, "timer done")   // delay(ms, value) 返回 Promise
  console.log(v)
}
main()
5
Promise { <pending> }     ← 顶层回显:main() 调用的返回值
timer done                ← 定时器到期后,await 继续执行

语法提示:异步函数写 async function 或 async 箭头 async () => {} 都行;顶层不能 await。

ES 模块 —— lib.js:

export const PI = 3.14
export function double(x) { return x * 2 }

main.js:

import { PI, double } from "./lib.js"
setTimeout(() => console.log("tick"), 10)
console.log(double(PI))
6.28
tick
内置对象

Array / String / Number / Object / Boolean / Math / JSON / Map / Set / WeakMap / WeakSet / Symbol / BigInt / RegExp / Proxy / Reflect / Iterator / Promise / ArrayBuffer / DataView(TypedArray 家族)/ WeakRef / FinalizationRegistry / 完整错误类型族 / Temporal(现代日期时间 API,本运行时不含 Date)。

响应式编程

Dart GetX 风格的 obs / computed / ever / once:

let count = obs(0)
ever(count, v => console.log("count =", v))   // 订阅时立即以当前值回调一次
count.value = 1
count.value = 2
count.value = 2   // 值未变化,不触发通知
count = 0
count = 1
count = 2
2                  ← 顶层回显:最后一条赋值表达式的值

以及 SolidJS 风格的 gx/solid 信号(createSignal / createEffect / createMemo / createResource / onMount / onCleanup)。


GUI 桌面应用

gx/gfx + gx/solid 提供 JSX 声明式 UI 与信号驱动的响应式更新,渲染器为纯 Go 软件光栅化 (无 cgo、无动态库依赖)。

用脚手架起工程
gox create my-app                          # 同为 goxjs create / npx @goxjs/goxjs create
gox create my-app --ts                     # TypeScript/TSX 版: .tsx 直接跑, 无 node 端构建
cd my-app && npm install && npm run dev

生成 package.json、入口 src/main.js、根组件 src/app.js、共享状态 src/store.js、 设计令牌 src/theme.js 与 src/components/ 下三个示例组件(计数器 / 待办列表 / 多状态), 把信号、元素级指令(each / show)、model 双向绑定与 Switch / Match 各演示一遍。 --ts 生成 TypeScript 版:入口改 src/main.tsx、附 tsconfig.json 与 src/gox.d.ts (IDE 类型提示),源码在加载时自动完成类型剥离(内嵌 esbuild 转译,JSX 原样保留)—— 没有 node 端构建步骤,类型检查交给 IDE,运行时零开销。

模板是 scaffold/template/ 下的真实文件,由 go:embed 嵌进二进制 (改模板要重新编译才生效;scaffold/scaffold_test.go 会保证生成出来的 JS/TS 仍能过 转译 + lexer → parser → compiler,gfx/scaffold_project_test.go 会真的把它挂载起来点一遍)。

最小手写版
import { h, render, createSignal } from "gox"        // 聚合入口:所有 gx/* 导出的并集
// 等价的细分写法:import { h, render } from "gx/gfx"; import { createSignal } from "gx/solid"

const [count, setCount] = createSignal(0)

render(
  <window title="Counter" width={400} height={300}>
    <column gap={8} padding={16}>
      <text font={20}>计数:{ count()}</text>
      <button onClick={() => setCount(c => c + 1)}>加一</button>
    </column>
  </window>
)
./gox counter.js          # 直接运行,弹出 400x300 窗口

点击按钮 → 信号更新 → 依赖它的属性/文本节点标脏 → 脏矩形合并后只重绘受影响区域。render() 返回窗口句柄,可调用多次开多窗口(各有独立元素树与焦点,全关才退出进程)。

GUI 能力清单
  • 63 个内置元素 —— 布局(column / row / grid / scroll / view)、表单(input / textarea / select / slider / checkbox / radio / switch)、数据展示(table / tree / list-item / tabs / pagination)、反馈(alert / tag / badge / avatar / empty / icon / spinner / skeleton / progress / tooltip)、弹层(dialog / drawer / toast)、 媒体(<video> / <audio>)、自绘画布、自绘菜单栏与右键菜单
  • 声明式界面是元素级指令 —— model 双向绑定、each 列表(keyed 复用)、show 条件保活显隐, 多分支用 gx/view 的 Switch + Match
  • 路由与屏幕 —— gx/router(路由表 / 参数匹配 / 三级守卫 / 懒加载 / 历史栈 / 多窗口作用域 / 折叠屏双栏)与 gx/screen(多显示器 / 窗口显示信息)
  • 媒体组件真的能播 —— 内核只定义宿主契约(nativeVideoHost / nativeAudioHost): 能挂载、能收事件、playing / muted / loop / volume / fit 全受控,后端没这块能力就明说缺 (封面占位 + 一次 onError({code:"unsupported"}) + canIUse("video"/"audio") 照实回答 false)。 Linux 默认构建挂 gfx/softmedia 软件后端:视频 GIF(含 disposal 合成)与 MJPEG,音频 WAV (PCM 8/16/24/32 位 + IEEE float),音频经 PulseAudio purego dlopen 输出(零 cgo)。 h264 / aac / mp3 留给平台媒体层,边界见 docs/media-decision.md

完整参考 —— 事件模型、内置元素属性表、弹性布局与装饰绘制、受控组件与输入法、过渡动画、 原生对话框 / 剪贴板 / 持久化存储 / 菜单栏、多窗口语义与平台差异 —— 见 docs/gui-guide.md,可直接运行的示例见 testdata/(counter_demo.js、form_demo.js、multiwindow_demo.js、 menu_demo.js、view_demo.js、router_demo.js、router_window_demo.js 等 108 个)。

路由与屏幕适配另有一份专门手册:docs/gui-router.md。


宿主能力模块

全局对象(无需 import)

fs / path / http / fetch / process / stats / crypto / hash:

const text = fs.readFileSync("a.txt")          // 同步:失败抛 JS 异常
const data = await fs.readFile("a.txt")        // 异步:无回调时返回 Promise
fs.mkdirSync("a/b", { recursive: true })

const server = http.createServer(function (req, res) {
  res.writeHead(200, { "Content-Type": "text/plain" })
  res.end("hello")
})
server.listen(0, function () { console.log(server.port) })

const res = await fetch("https://example.com")
console.log(res.status, await res.text())

异步 I/O 的实现模型是跨 goroutine I/O + 投递回 VM 单线程:网络读写不触碰 VM 状态,结果经 零延时定时器回到事件循环执行 JS 回调,因此回调里访问 VM 无需加锁。

一份完整的 HTTP 示例(起服务于端口 0、路由/查询参数/JSON 请求体/404-405-500 各状态码、 回调式 http.get · http.request 与 Promise 式 fetch 对照、异步响应与 server.close 收尾): testdata/http_demo.js —— gox testdata/http_demo.js 即可运行。

摘要与随机数 —— crypto 与 hash 底层走 Go 标准库 crypto/*,零 cgo:

hash("gox");                                 // 同步 → 小写 hex (缺省 SHA-256)
hash("gox", "SHA-512");                      // 算法名大小写/连字符不敏感
const ab = await crypto.subtle.digest("SHA-256", "gox");   // Promise → ArrayBuffer (32 字节)
crypto.randomUUID();                                       // RFC 4122 v4
crypto.getRandomValues(new Uint8Array(16));                // 原地填充, 上限 64KB
项 说明
算法 hash() 支持 MD5 / SHA-1 / SHA-256 / SHA-512;crypto.subtle.digest() 只认 SHA-1 / SHA-256 / SHA-512
参数序 hash(data, algo) 而 crypto.subtle.digest(algo, data)
不做的事 加解密 / 签名 / KDF 不在本模块内
错误 不支持的算法或入参一律 TypeError,不会静默回退
gx/* 内置模块(需要 import)

注册表共 23 个模块:gox(聚合入口)、gx/solid、gx/gfx、gx/view、gx/router、 gx/screen、gx/viewport、gx/dialog、gx/storage、gx/update、gx/crypto、gx/theme、 gx/dev、gx/a11y、gx/device、gx/app、gx/geo、gx/media、gx/permission, 以及全局侧同样可 import 的 fs / http / path / process。

原生能力层六个模块共用一个宿主契约(NativeHost)。三种调用形态要在写代码时区分开:

import { deviceInfo, battery, isOnline, canIUse } from "gx/device";   // 拉取型 + 上报型
import { getLocation, watchLocation } from "gx/geo";
import { takePhoto } from "gx/media";                                  // 动作型

const info = deviceInfo();                    // 同步可得
console.log(info.platform, info.model, battery().level + "%", isOnline());

if (canIUse("camera")) {                      // 事前判断能力,不要靠 catch 兜底
  const photo = await takePhoto({ count: 1 });      // 失败会 reject,带 errCode
}

const stop = watchLocation((loc) => console.log(loc.latitude, loc.longitude));
try {
  await getLocation({ highAccuracy: true });
} catch (e) {
  if (e.errCode === "permission-denied") console.warn(e.message);
}

桌面后端直接实现其中能实现的部分(电量 / 网络 / 亮度 / 屏幕常亮 / 打开系统设置页), 给不出的一律诚实报 unsupported。移动端同样实现这套 NativeHost 契约,不必改内核 —— Android 壳(Kotlin)与 iOS 壳(Swift)的六个模块基本齐备:Android 已在模拟器上实测通过 (上屏 / 触摸 / IME / 安全区 / 折叠屏),iOS 壳工程与构建脚本(含 TestFlight 打包)齐备、真机验收待做; 鸿蒙壳(ArkTS)目前只通了安全区与折叠上报两条纯上报通道,六个能力模块仍是桩。 逐方法(模块 × 平台)状态表、壳构建说明与首帧自检清单见 app/NATIVE-HOST.md; 三种形态的完整演示:testdata/native_demo.js。

三端验证范围(CI 矩阵口径)—— 能力矩阵按实际验证面写,不夸:

平台 构建冒烟(每次 push) 设备回归 折叠语义
Android mobile-smoke.yml 的 android job:libgox.so → assembleDebug APK android-fold-emulator job:pixel_fold 模拟器(API 34+,真能切铰链)上跑 CLOSED → HALF_FOLDED → OPENED 矩阵 + 截屏存档;真机侧 scripts/mobile-device-accept.sh --rounds 20 ✅ 模拟器已覆盖折叠态切换
iOS mobile-smoke.yml 的 ios job:xcodebuild 模拟器包 ios-device-matrix job:iPhone 16 / 16 Pro Max / iPad 三机型 × 竖横屏,simctl 截屏存档 ⚠️ 模拟器没有折叠屏:reservedRegions(iOS 27.1+)在模拟器只报 flat;真机验收待做
鸿蒙 交叉编译 + 宿主契约测试 + assembleHap 构建通过 — ❌ 模拟器验收待做(HAP 签名是硬阻塞);不写「已支持鸿蒙」

打包与工具链

jsbuild:脚本 → 独立可执行文件

jsbuild(packager/)把入口脚本及其相对 import 的模块嵌入一个生成的 Go 工程,编译成单文件 可执行程序,自带完整运行时:

go run ./packager app.js -o app.exe                    # CLI 应用
go run ./packager counter.js --gui -o counter.exe      # GUI 应用
go run ./packager app.js --gui --target linux/amd64    # 纯 Go 交叉编译,无需目标机工具链

各平台的分发注意事项(Windows 图标与签名、Linux 打包格式、macOS .app bundle)见 docs/desktop-distribution.md。

命令行参数:打包产物与 gox <file> <args> 保持同一口径 —— process.argv 恒为 [可执行文件, 脚本路径, ...用户参数],用户参数始终从索引 2 开始。 于是同一份脚本源码跑和打包跑都能用 process.argv[2] 取到第一个用户参数。

多平台配置(gox.json / 权限 / 图标 / 证书)

gox create 生成的工程自带 gox.json(应用名、appId、版本、权限、各平台子配置) 与完整的 Android / iOS / 桌面资源骨架 + 默认图标:

命令 作用
gox sync 把 gox.json 里的 permissions 幂等注入 AndroidManifest 与 iOS Info.plist
gox icon 以 assets/icon.png(1024×1024)为单源生成 Android mipmap、iOS AppIconSet、Windows .ico、macOS .icns、favicon
gox cert 一键生成平台签名证书(纯 Go 实现,无需 JDK/openssl):android / windows / harmony / ios
gox build <android|ios|windows|macos> 统一构建入口(sync → icon → 平台打包);--release 走签名发布包

字段说明、权限清单与图标规范见 docs/platform-config.md。 脚本运行时的权限模型(--allow-fs-write 等,默认最小权限)见 docs/permissions.md。

npm 包信息
项 值
包名 / 命令 @goxjs/goxjs → goxjs
内含平台 macOS x64 / arm64、Linux x64 / arm64、Windows x64
Node 版本 >=14(仅用于分发二进制,运行期不依赖 Node)
版本真源 gox-npm 的 package.json 的 version 字段
移动端子包 @goxjs/goxjs-mobile-<platform>-<abi>(4 个,按需安装,不在主包依赖里)

技术架构

编译与执行管线
 .js 源码
    │  lexer        词法分析
    ▼
 Token 流
    │  parser       语法分析(含 JSX 降级)
    ▼
 AST (ast/)
    │  compiler     符号表解析 + 字节码生成 (常量池)
    ▼
 字节码 (bytecode/)   定长 3 字节: [opcode 1B][operand 2B 大端],160 个操作码
    │  vm           栈式调用帧执行 + 定时器事件循环 + 模块加载
    ▼
 结果
关键设计决策
  • 定长指令编码 —— 无需变长解码状态机,直取操作数;常量池承担大对象引用。
  • 单线程 VM + 跨 goroutine 调度 —— http.createServer 等网络回调跨 goroutine 捕获后调度回 VM 单线程执行,无需锁。挂起任务保活事件循环,计数归零才允许进程退出。
  • 可选能力走可选接口 —— GUI 后端通过 Surface + WindowFactory 抽象,换后端零改动脚本; 平台独有能力(IME、剪贴板、原生对话框、视频 / 音频)按后端探测,缺失时诚实降级而非报错。
  • 固定分片保证合规率可复现 —— test262 runner 的分片数钉死 128 且与 -jobs 解耦: 引擎有包级可变状态,分片数若跟着并发度变,每个用例的"同进程邻居"就变,通过数会随 -jobs 翻转。
目录结构
目录 职责
lexer/ 词法分析器(含 JSX 词法支持)
parser/ 语法分析器,生成 AST(含 JSX 语法降级)
ast/ AST 节点定义
compiler/ AST → 字节码编译器(含符号表)
bytecode/ 操作码与字节码格式定义
vm/ 栈式字节码虚拟机(调用帧、模块加载、定时器调度)
object/ 运行时对象系统(Number / Array / Map / Promise / Observable…)
runtime/ 全局环境 Environment
stdlib/ 标准库与宿主 API 实现(含 gx/solid 响应式信号)
gfx/ 自研 GUI 渲染层(软件光栅化、布局、命中测试、win32 / X11 / cocoa 后端)
packager/ jsbuild 打包器(GUI 应用、交叉编译)
scaffold/ gox create 的项目脚手架:template/ 是真实文件(go:embed 进二进制)
tools/ 开发工具:tstransform/(TS/TSX → JS)、icongen/(图标)、certgen/(证书)、update/(自更新)、perf/(基准)
test/ 测试相关:bench/ 性能剖析基准
testdata/ 可直接运行的示例脚本(108 个)
app/ 移动端壳工程与 NATIVE-HOST.md 契约文档
docs/ 对外文档
scripts/ 构建与检查脚本:build-npm.sh、check-registries.py、check-imports.py、test262-cluster.py 等
.github/workflows/ CI:ci.yml 常规闸门、release.yml 发版、pages.yml 官网、test262.yml 合规率、mobile-smoke.yml 三端矩阵

性能

数字来自 CI runner 上的公开基准(bench-results/,AMD EPYC 7763)与官网实测页(Apple M2)。 含未达标项 —— 口径与阈值全部公开。

指标 gox goja(纯解释器对照) node / V8(JIT 对照)
启动耗时 4 ms 3 ms 25 ms
fib28 纯计算 631 ms 200 ms 34 ms
1 万定时器 168 ms 116 ms 139 ms
常驻内存 RSS 25 MB 10 MB 42 MB

结论:启动与内存与同类解释器同级;纯计算算力未达 goja 同级(fib28 慢约 3 倍), 这是纯解释器 + 无 JIT 的直接代价,不作为优化目标。UI 无 GC 毛刺达标 (重压聚合 p95 帧间隔 5.95ms ≤ 16.7ms;单次 GC 停顿 max 1.382ms ≤ 5ms)。

虚拟化长列表(<scroll vlist>)与行数脱钩:十万行首帧 89ms vs 全量 2187ms(约 24 倍), 滚动帧恒在 0.11~0.17ms vs 全量 999ms。

完整数字、跑法与判定阈值见 官网 · 性能与合规。


文档地图

下表只列 docs/ 里已定稿、对外发布的文档。

文档 内容
docs/tutorial.md 实战教程:API 调用与参数、内置模块导入、gox create 建工程、路由定义与注册
docs/gui-guide.md GUI 开发指南:元素/事件参考、布局、动画、宿主能力、示例索引
docs/gui-router.md 路由与屏幕适配手册
docs/gui-patterns.md 用户态模式手册(路由、状态、主题等惯用法)
docs/gui-model-binding.md model 双向绑定:接口设计、语义表、与 Vue 的对照、反例
docs/foldable-adaptation.md 折叠屏适配教程:纯读数短路订阅的坑、折痕避让单位换算、三端验证范围
docs/platform-config.md 多平台配置:gox.json 字段、权限清单、图标规范、gox build 各目标
docs/desktop-distribution.md 桌面应用分发:图标、签名、各平台打包格式
docs/js-runtime-api-tutorial.md 运行时 API 教程:函数类型、回调桥、内存管理、新增 API 的流程
docs/permissions.md 脚本运行时权限模型
docs/compat-matrix.md 主流 npm 库实测兼容性
docs/npm-release.md @goxjs/goxjs 发版手册
官网 安装、运行脚本、写 GUI 应用与打包的在线教程(中英双语)

开发与测试

go test ./...          # 运行全部测试(308 个测试文件 / 2419 个测试函数)
go run ./test/bench    # 生成 cpu.prof 性能剖析

由于零 cgo,go test -race 在本项目不可用(Go 会要求 cgo 支持)。

深入参与开发(新增标准库 API、理解回调桥与内存管理)请阅读 docs/js-runtime-api-tutorial.md。


发版流程

@goxjs/goxjs 由 GitHub Actions 自动发布(.github/workflows/release.yml), 认证走 npm Trusted Publishing (OIDC),仓库里不需要任何 secret / token:

# 1. 改版本号 —— npm/package.json 的 version 是唯一真源,npm/ 是独立仓库:
$EDITOR npm/package.json
git -C npm commit -am "chore(release): 0.15.0" && git -C npm push
# 2. 回主仓打 tag 并推送 —— tag 必须与 gox-npm 默认分支的版本一致
git tag v0.15.0 && git push Gox v0.15.0
# 3. 剩下交给 CI:交叉编译五平台二进制 → 打包校验 → OIDC 发布 → 自动打 tag + 建 Release
  • 触发:push 到 main、推送 v* 标签、发布 Release,以及手动 workflow_dispatch
  • 幂等:CI 先查 registry,该版本已存在则跳过并留一条 notice —— 重复推送不会误发
  • 版本号:日常提交不会触发真发布;cmd/gox/main.go 的 const version 与 npm/package.json 须同号(scripts/check-registries.py 卡死)
  • 完整手册:见 docs/npm-release.md

贡献指南

欢迎提交 Issue 与 Pull Request —— 完整的参与方式、本地闸门、与本仓库特有的 「改一处、同步多处」纪律,见 CONTRIBUTING.md; 行为准则见 CODE_OF_CONDUCT.md。首次贡献可从带 good first issue 标签的 issue 入手。

开发环境:Go 1.26.2+,零 cgo(无需 C 工具链)。提交前请确保:

gofmt -l .             # 应无输出
go build ./...
go vet ./...
go test ./...
python3 scripts/check-registries.py   # 注册表一致性(CI 同款闸门)
python3 scripts/check-imports.py      # 文档/示例 import 与导出表一致(CI 同款闸门)

提交规范:沿用仓库现有的 Conventional Commits 风格,scope 用受影响的模块或主题,中文描述:

feat(gfx): 新增 <slider> 的键盘调节支持
fix(vm): 修正 SET_INDEX 对非对象类型静默丢弃的问题
docs(gui-guide): 补充多窗口事件泵的轮询语义
ci(release): 打包闸改回 tar 校验,不再解析 npm 的输出

新增内置 GUI 组件时须同步四处(漏改会静默失效,不出编译错误):

位置 作用
gfx/node.go 的 knownTags 注册标签名,否则 h() 会打印"未知标签"警告
gfx/layout.go 的 intrinsicSize 声明固有尺寸
gfx/layout.go 的布局分派 声明子节点如何参与布局
gfx/raster.go 的 drawNode 实现绘制;被 case 截走的标签需自行补画 background / border

其他约定:

  • 事件回调统一经 callHandler 分发;涉及窗口的操作从节点出发经 appOfNode(n) 取所属窗口(支持多窗口)
  • gx/* 的 JSX 属性与子节点在调用当场求值一次 —— 需要响应式就必须传函数(value: () => sig()), 子节点写 {() => sig()} 而不是 {sig()}),传值只是一张快照 —— 而且子节点写成快照 是全静默的(属性误用有警告,子节点没有),见 docs/gui-guide.md §8.1
  • 示例脚本放在 testdata/ 并确保可直接运行

许可证

MIT License

Directories

Path Synopsis
Package bytecode 定义了虚拟机的字节码指令集。
Package bytecode 定义了虚拟机的字节码指令集。
cmd
gox command
cmd_build.go 实现 `gox build <target>`: 统一构建入口。
cmd_build.go 实现 `gox build <target>`: 统一构建入口。
goxwasm command
非 js/wasm 平台的占位:让 `go build ./...` / `go vet ./...` 在本机不至于因 "build constraints exclude all Go files" 报错。
非 js/wasm 平台的占位:让 `go build ./...` / `go vet ./...` 在本机不至于因 "build constraints exclude all Go files" 报错。
Package compiler 将 AST 编译为字节码。
Package compiler 将 AST 编译为字节码。
Package config 定义并解析 Gox 工程的项目级配置文件 gox.json。
Package config 定义并解析 Gox 工程的项目级配置文件 gox.json。
gfx
Package gfx 提供 Gox 的自研 GUI 渲染层 (P2: Windows 软件渲染)。
Package gfx 提供 Gox 的自研 GUI 渲染层 (P2: Windows 软件渲染)。
backend
Package backend 按构建平台选择窗口后端: 此文件在 Linux 构建时 注册 gfx/x11 (纯 Go X11 协议绑定 jezek/xgb, 无 cgo)。
Package backend 按构建平台选择窗口后端: 此文件在 Linux 构建时 注册 gfx/x11 (纯 Go X11 协议绑定 jezek/xgb, 无 cgo)。
bind
Package bind 是 gfx 的 JS 绑定层: 把内核能力以 object.RegisterBuiltinModule 注册成脚本可 import 的 "gx/xxx" 模块。
Package bind 是 gfx 的 JS 绑定层: 把内核能力以 object.RegisterBuiltinModule 注册成脚本可 import 的 "gx/xxx" 模块。
cocoa
Package cocoa 是 gfx 的 macOS 窗口后端 —— 基于 purego (纯 Go, 无 cgo) 的 objc runtime 桥, 直接驱动 AppKit。
Package cocoa 是 gfx 的 macOS 窗口后端 —— 基于 purego (纯 Go, 无 cgo) 的 objc runtime 桥, 直接驱动 AppKit。
gpu
Package gpu 的默认轨: 空实现。
Package gpu 的默认轨: 空实现。
hostbridge
Package hostbridge 是移动端三端 (android / ios / harmony) **共用**的宿主协议层。
Package hostbridge 是移动端三端 (android / ios / harmony) **共用**的宿主协议层。
hosttest
Package hosttest 是「宿主契约」的**平台无关**测试套件 (无 build tag)。
Package hosttest 是「宿主契约」的**平台无关**测试套件 (无 build tag)。
mobile
Package mobile 是移动端 (Android / iOS) 后端的**可移植内核部分**: 事件队列、触摸→指针映射、帧上传钩子、显示器 (density) 上报、事件泵唤醒。
Package mobile 是移动端 (Android / iOS) 后端的**可移植内核部分**: 事件队列、触摸→指针映射、帧上传钩子、显示器 (density) 上报、事件泵唤醒。
softmedia
Package softmedia 是 gfx 的**软件媒体后端** —— 用纯 Go 标准库可解码的 格式, 把 `<video>` / `<audio>` 从"契约 + 降级"变成"真的能播"。
Package softmedia 是 gfx 的**软件媒体后端** —— 用纯 Go 标准库可解码的 格式, 把 `<video>` / `<audio>` 从"契约 + 降级"变成"真的能播"。
x11
多显示器枚举 (factory 级 displayProvider 的 x11 实现, M4 2026-10-02)。
多显示器枚举 (factory 级 displayProvider 的 x11 实现, M4 2026-10-02)。
Package object 定义了 JavaScript 运行时中的所有值类型。
Package object 定义了 JavaScript 运行时中的所有值类型。
jsbuild 将 JavaScript 文件打包成独立的可执行程序。
jsbuild 将 JavaScript 文件打包成独立的可执行程序。
Package permissions 实现 Gox 的脚本权限模型(v1,Deno 风格)。
Package permissions 实现 Gox 的脚本权限模型(v1,Deno 风格)。
Package runtime 提供 JavaScript 运行时的基础组件: - Environment: 作用域环境链 (块作用域 + const 检查) - Iterator: 迭代器协议 (for...of)
Package runtime 提供 JavaScript 运行时的基础组件: - Environment: 作用域环境链 (块作用域 + const 检查) - Iterator: 迭代器协议 (for...of)
Package scaffold 是 Gox 自带的项目脚手架 —— 对标前端的 `npm create vite`: 一条命令铺出一个能直接跑起来的默认 GUI 工程。
Package scaffold 是 Gox 自带的项目脚手架 —— 对标前端的 `npm create vite`: 一条命令铺出一个能直接跑起来的默认 GUI 工程。
gx/crypto: 摘要与随机数的**最小可用集** (看板 ru3TZK)。
gx/crypto: 摘要与随机数的**最小可用集** (看板 ru3TZK)。
test
bench command
tools
bcdump command
bcdump 是调试工具: 编译一个 JS 源文件并反汇编主程序字节码。
bcdump 是调试工具: 编译一个 JS 源文件并反汇编主程序字节码。
certgen
Package certgen 为 `gox cert` 提供纯 Go 的证书生成能力。
Package certgen 为 `gox cert` 提供纯 Go 的证书生成能力。
cpu-prof command
cpu-prof: 临时性能剖面工具 — 跑一份 JS 脚本并输出 CPU profile。
cpu-prof: 临时性能剖面工具 — 跑一份 JS 脚本并输出 CPU profile。
icongen
Package icongen 负责"单源图标 + 平台资源"的全部纯 Go 生成逻辑:
Package icongen 负责"单源图标 + 平台资源"的全部纯 Go 生成逻辑:
initpurity command
Command initpurity 是「包级 init() 不得做平台 IO / 宏内核调用」的静态闸门。
Command initpurity 是「包级 init() 不得做平台 IO / 宏内核调用」的静态闸门。
memdrift
Package memdrift 是 GUI 常驻进程的长时内存漂移测试(T3-3)。
Package memdrift 是 GUI 常驻进程的长时内存漂移测试(T3-3)。
perf
Package perf 是 M9「并发与性能」的**可复现性能基准设施**。
Package perf 是 M9「并发与性能」的**可复现性能基准设施**。
propdump command
Command propdump 从 gfx/*.go 的**属性读取点**反推「内核认得哪些属性 / 事件」, 供 scripts/gen-props-golden.py 生成 docs/props.golden.json(看板 rgQsDD)。
Command propdump 从 gfx/*.go 的**属性读取点**反推「内核认得哪些属性 / 事件」, 供 scripts/gen-props-golden.py 生成 docs/props.golden.json(看板 rgQsDD)。
rssmon command
tools/rssmon —— GUI 常驻进程 RSS 曲线采样器(r95WjJ)。
tools/rssmon —— GUI 常驻进程 RSS 曲线采样器(r95WjJ)。
tstransform
Package tstransform 把 TypeScript / TSX 源码转译为等价的 JS 源码。
Package tstransform 把 TypeScript / TSX 源码转译为等价的 JS 源码。
update
Package update 实现 Gox 桌面应用的自动更新 (gx/update v1.1)。
Package update 实现 Gox 桌面应用的自动更新 (gx/update v1.1)。
Package vm 实现了 JavaScript 的栈式字节码虚拟机。
Package vm 实现了 JavaScript 的栈式字节码虚拟机。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL