feat: fun-cli v1.0.0 — gen/run/docs 三命令,fun 框架零改动
- fun gen [ts|go]:AST 扫描 BindService* 调用,临时生成 go test 辅助采集, 经 fun v1.3.7 公开 API 生成,产物与内置 GenCode 字节级一致(回归测试锁定) - fun run:热更新开发循环,先编译成功再切换,失败保留旧进程 - fun docs:浅色标准文档站——参数表/State 添加字段/请求 JSON 可编辑/ 响应格式化/NDJSON 流式逐行展示,反向代理 /cell 同源无 CORS - 元数据:TS 产物两遍解析重建(枚举/可空/流式工厂/嵌套引用全支持) - 纯标准库实现,零第三方依赖
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
package main
|
||||
|
||||
// 采集辅助:在 BindService 调用所在的包目录临时生成 fun_gen_helper_test.go
|
||||
// (测试文件可访问包内类型,天然解决"服务定义在 package main"不可 import 的问题),
|
||||
// go test 执行完毕后删除。fun 框架本体零改动,只用其公开 API
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const helperTestFile = "fun_gen_helper_test.go"
|
||||
const helperTestName = "TestFunGenHelper"
|
||||
|
||||
// helperTarget 一个待生成辅助测试的包目录
|
||||
type helperTarget struct {
|
||||
Dir string
|
||||
Pkg string
|
||||
Refs []serviceRef
|
||||
}
|
||||
|
||||
type helperRef struct {
|
||||
Alias string // 引用别名(&pkg.Svc 的 pkg);同包引用为空
|
||||
Import string // 引用的导入路径
|
||||
TypeName string
|
||||
}
|
||||
|
||||
// runFunGen 扫描绑定调用,生成辅助测试并执行:
|
||||
// lang: "ts" / "go" / "both";out 为 GenCode 输出目录(会被 fun 清空重建)
|
||||
func runFunGen(moduleRoot, lang, out string) error {
|
||||
targets, err := scanBindTargets(moduleRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(targets) == 0 {
|
||||
return fmt.Errorf("未在模块中找到 BindService/BindServiceForGen 调用。" +
|
||||
"请确认服务注册代码,或用 -from 直接指定元数据 JSON 文件")
|
||||
}
|
||||
|
||||
// 清理保险:先移除可能的历史残留
|
||||
defer cleanupHelpers(targets)
|
||||
cleanupHelpers(targets)
|
||||
|
||||
pkgs := make([]string, 0, len(targets))
|
||||
for _, t := range targets {
|
||||
if err := writeHelperTest(moduleRoot, t); err != nil {
|
||||
return err
|
||||
}
|
||||
rel, err := filepath.Rel(moduleRoot, t.Dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := "./" + filepath.ToSlash(rel)
|
||||
pkgs = append(pkgs, pkg)
|
||||
}
|
||||
|
||||
args := append([]string{"test", "-vet=off", "-count=1", "-run", "^" + helperTestName + "$"}, pkgs...)
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Dir = moduleRoot
|
||||
cmd.Env = append(os.Environ(), "FUN_OUT="+out, "FUN_LANG="+lang)
|
||||
cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
cleanupHelpers(targets)
|
||||
return fmt.Errorf("采集执行失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanupHelpers(targets []helperTarget) {
|
||||
for _, t := range targets {
|
||||
os.Remove(filepath.Join(t.Dir, helperTestFile))
|
||||
}
|
||||
}
|
||||
|
||||
func writeHelperTest(moduleRoot string, t helperTarget) error {
|
||||
// 汇总需要的外部导入(去重,按路径排序)
|
||||
imports := map[string]string{} // alias → path
|
||||
for _, r := range t.Refs {
|
||||
if r.Alias != "" {
|
||||
imports[r.Alias] = r.Import
|
||||
}
|
||||
}
|
||||
paths := make([]string, 0, len(imports))
|
||||
for _, p := range imports {
|
||||
paths = append(paths, p)
|
||||
}
|
||||
sort.Strings(paths)
|
||||
aliasOf := func(path string) string {
|
||||
for a, p := range imports {
|
||||
if p == path {
|
||||
return a
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var imp, binds strings.Builder
|
||||
imp.WriteString("\t\"os\"\n\t\"testing\"\n\n\t\"github.com/cyi-cc/fun\"\n")
|
||||
for _, p := range paths {
|
||||
fmt.Fprintf(&imp, "\t%s %q\n", aliasOf(p), p)
|
||||
}
|
||||
for _, r := range t.Refs {
|
||||
if r.Alias != "" {
|
||||
fmt.Fprintf(&binds, "\tf.BindServiceForGen(&%s.%s{})\n", r.Alias, r.TypeName)
|
||||
} else {
|
||||
fmt.Fprintf(&binds, "\tf.BindServiceForGen(&%s{})\n", r.TypeName)
|
||||
}
|
||||
}
|
||||
|
||||
src := fmt.Sprintf(`// fun-cli 临时采集辅助(自动生成,执行完即删除)
|
||||
package %s
|
||||
|
||||
import (
|
||||
%s)
|
||||
|
||||
func %s(t *testing.T) {
|
||||
f := fun.New()
|
||||
%s fun.SetOutput(os.Getenv("FUN_OUT"))
|
||||
switch os.Getenv("FUN_LANG") {
|
||||
case "ts":
|
||||
fun.GenCode(fun.GenTs{})
|
||||
case "go":
|
||||
fun.GenCode(fun.GenGo{})
|
||||
default:
|
||||
fun.GenCode(fun.GenGo{}, fun.GenTs{})
|
||||
}
|
||||
}
|
||||
`, t.Pkg, imp.String(), helperTestName, binds.String())
|
||||
return os.WriteFile(filepath.Join(t.Dir, helperTestFile), []byte(src), 0o644)
|
||||
}
|
||||
Reference in New Issue
Block a user