约定:文件名 = 主类型名;同一个类型要拆多个文件时用 类型_子项.go。
engine.go 355 → 231 行。原来混了三件不相干的事
engine_globals.go ← bind / lazyGlobal / freeze / defineReadOnly(96 行)
它是「Go 值 → 只读 JS 全局」的转换层,跟脚本缓存毫无关系
engine_console.go ← console.go,跟上面是同一主题
script.go 325 → 100 行,只留公开方法
script_vm.go ← VM 的取、还、装载。上个 commit 合一的三条路径现在住一起
script_static.go ← static.go
errors.go 364 → 150 行
errors_goja.go ← goja 错误的翻译层
errors_hints.go ← missingGlobalHint,一份 JS 运行时知识库,跟错误分类是
两回事;拆出来之后 missing_global_test.go 才有对应源文件
bundle_finalize.go ← esmwrap.go
bundle.go 收下 validIdent / hashVersion(原来住在 engine.go)
Engine.New 原来排在所有私有函数之后,挪到导出方法那一段。
classify 83 → 54 行:四个 errors.As 分支各手搓一个 8 字段的 &Error{},脚本上下文
那三行重复了 4 遍,抽出 gojaError 构造器。
顺带修四处注释漂移:
Session/会话 代码里叫 Instance,注释里大面积残留。engine_globals.go 那条
「注入会话全局对象失败」还是用户可见文案,而公开 API 里根本
没有「会话」这个概念
Extension 文档示例写 Module() string,接口是 Module() (path, source string)。
这是唯一一段教人写扩展的文档,照抄编译不过
doc.go 的 freeze 说白名单「逐层拷贝成只读对象,不会跨 VM 共享可变的 Go map」,
但那只对 map[string]any 成立。结构体指针和 slice 是**共享同一个
对象**的——扩展走的正是这条路,不该被当成隔离保证
49 lines
2.0 KiB
Go
49 lines
2.0 KiB
Go
package jscriptx
|
||
|
||
// Extension 是一个作用域级扩展:给脚本添一个全局对象。
|
||
//
|
||
// 扩展实例由调用方自己创建,跟着 ctx 走:
|
||
//
|
||
// st := jscriptx.NewStore()
|
||
// tx := myTx(db)
|
||
// ctx = jscriptx.WithScope(ctx, jscriptx.ScopeExtensions(st, tx))
|
||
//
|
||
// obj, err := ctrl.New(ctx) // 脚本里能用 store 和 tx
|
||
// defer obj.Close()
|
||
//
|
||
// st.Get("count") // Go 侧拿的就是同一份,不用再取回
|
||
//
|
||
// 同一个 ctx 下 New 出来的所有实例共享同一份扩展对象——这就是多个 controller
|
||
// 共享数据的方式:共享的是 Go 侧对象,不是共用 Runtime。
|
||
//
|
||
// 业务写自己的扩展只要实现这三个方法:
|
||
//
|
||
// type tx struct{ conn *sql.Tx }
|
||
//
|
||
// func (t *tx) Name() string { return "tx" }
|
||
// func (t *tx) Bindings() map[string]any {
|
||
// return map[string]any{"commit": t.conn.Commit, "rollback": t.conn.Rollback}
|
||
// }
|
||
// func (t *tx) Module() (path, source string) { return "@fsdpf/tx", txTypings }
|
||
type Extension interface {
|
||
// Name 是它在脚本里的全局名,必须是合法的 JS 标识符。
|
||
// 跟白名单(WithGlobals)同名时以白名单为准。
|
||
Name() string
|
||
|
||
// Bindings 是暴露给脚本的方法集。方法名**大写开头**,跟脚本里能碰到的
|
||
// 其它东西保持一致——传进来的 Go 对象(m.GetCode()、res.GetDBTable())
|
||
// 用的都是 Go 的方法名,扩展再用小写的话,同一行代码里两种风格混着写。
|
||
Bindings() map[string]any
|
||
|
||
// Module 返回配套 TypeScript 模块的 import 路径和源码,让脚本能拿到类型:
|
||
//
|
||
// import store from "@jscriptx/store"
|
||
//
|
||
// 路径要加 scope 前缀,免得跟 node_modules 里的包撞名。用什么 scope 自己定,
|
||
// 框架层那套用的是 @jscriptx/,业务自己的可以用 @fsdpf/ 之类。
|
||
// 路径返回空字符串表示不提供模块,脚本只能用全局变量的写法。
|
||
//
|
||
// 模块本身只是个门面:把全局对象转发出来,附上类型声明。真正的实现在 Go 侧。
|
||
Module() (path, source string)
|
||
}
|