package main import ( "encoding/json" "os" "path/filepath" "strings" ) // PackSuggestItem 推荐的一条打包命令。 type PackSuggestItem struct { Name string `json:"name"` Cmd string `json:"cmd"` } // PackSuggest 根据目录识别出的项目类型与推荐命令。 type PackSuggest struct { Kind string `json:"kind"` Label string `json:"label"` Items []PackSuggestItem `json:"items"` } // SuggestPackCommands 分析项目目录,给出打包/构建推荐命令(本地,不同步)。 func (a *App) SuggestPackCommands(dir string) PackSuggest { return detectPackSuggestions(strings.TrimSpace(dir)) } func detectPackSuggestions(dir string) PackSuggest { out := PackSuggest{Kind: "other", Label: "通用", Items: nil} if dir == "" { return out } if st, e := os.Stat(dir); e != nil || !st.IsDir() { return out } // —— 最具体优先 —— if isWails3Project(dir) { out.Kind, out.Label = "wails3", "Wails 3" out.Items = []PackSuggestItem{ {Name: "开发热更", Cmd: "wails3 task dev"}, {Name: "Windows 构建", Cmd: "wails3 task windows:build"}, {Name: "完整安装包", Cmd: "wails3 task package"}, {Name: "仅打包后端", Cmd: "wails3 task api:package"}, } return out } if fileExists(dir, "wails.json") || hasWailsV2Mod(dir) { out.Kind, out.Label = "wails", "Wails 2" out.Items = []PackSuggestItem{ {Name: "开发", Cmd: "wails dev"}, {Name: "生产构建", Cmd: "wails build"}, {Name: "生产构建(压缩)", Cmd: "wails build -clean -upx"}, } return out } if fileExists(dir, "package.json") { pm := npmPM(dir) pkg := readNPMPackage(dir) run := func(script string) string { switch pm { case "pnpm": return "pnpm " + script case "yarn": return "yarn " + script case "bun": return "bun run " + script default: return "npm run " + script } } if isVbenProject(dir, pkg) { out.Kind, out.Label = "vben", "Vben Admin" items := []PackSuggestItem{} for _, s := range []struct{ name, key string }{ {"构建", "build"}, {"构建 Antd", "build:antd"}, {"构建 Ant Design Vue", "build:ant"}, {"构建 Element", "build:ele"}, {"构建 Naive", "build:naive"}, {"预览", "preview"}, {"类型检查", "typecheck"}, } { if pkgHasScript(pkg, s.key) { items = append(items, PackSuggestItem{Name: s.name, Cmd: run(s.key)}) } } if len(items) == 0 { items = append(items, PackSuggestItem{Name: "构建", Cmd: run("build")}) } out.Items = items return out } if isNextProject(dir) { out.Kind, out.Label = "next", "Next.js" out.Items = packScriptsOr(pkg, run, []string{"build", "export", "start"}, []PackSuggestItem{ {Name: "生产构建", Cmd: run("build")}, }) return out } if isNuxtProject(dir) { out.Kind, out.Label = "nuxt", "Nuxt" out.Items = packScriptsOr(pkg, run, []string{"build", "generate", "preview"}, []PackSuggestItem{ {Name: "生产构建", Cmd: run("build")}, {Name: "静态生成", Cmd: run("generate")}, }) return out } if isElectronProject(pkg) { out.Kind, out.Label = "electron", "Electron" out.Items = packScriptsOr(pkg, run, []string{"build", "dist", "package", "make", "pack"}, []PackSuggestItem{ {Name: "打包", Cmd: run("build")}, }) return out } if isVueProject(dir, pkg) { out.Kind, out.Label = "vue", "Vue" out.Items = packScriptsOr(pkg, run, []string{"build", "build:prod", "build:pro", "preview"}, []PackSuggestItem{ {Name: "生产构建", Cmd: run("build")}, }) return out } if isReactProject(pkg) { out.Kind, out.Label = "react", "React" out.Items = packScriptsOr(pkg, run, []string{"build", "build:prod", "preview"}, []PackSuggestItem{ {Name: "生产构建", Cmd: run("build")}, }) return out } out.Kind, out.Label = "node", "Node.js" out.Items = collectNPMBuildScripts(pkg, run) if len(out.Items) == 0 { out.Items = []PackSuggestItem{{Name: "构建", Cmd: run("build")}} } return out } if fileExists(dir, "go.mod") { out.Kind, out.Label = "go", "Go" name := filepath.Base(dir) out.Items = []PackSuggestItem{ {Name: "编译当前平台", Cmd: "go build -o bin/" + name + " ."}, {Name: "Linux amd64", Cmd: "set GOOS=linux&& set GOARCH=amd64&& go build -o bin/" + name + " ."}, {Name: "测试", Cmd: "go test ./..."}, {Name: "整理依赖", Cmd: "go mod tidy"}, } if fileExists(dir, "Makefile") || fileExists(dir, "makefile") { out.Items = append([]PackSuggestItem{ {Name: "make build", Cmd: "make build"}, {Name: "make package", Cmd: "make package"}, }, out.Items...) } return out } if fileExists(dir, "Cargo.toml") { out.Kind, out.Label = "rust", "Rust" out.Items = []PackSuggestItem{ {Name: "Release 构建", Cmd: "cargo build --release"}, {Name: "检查", Cmd: "cargo check"}, {Name: "测试", Cmd: "cargo test"}, } return out } if fileExists(dir, "pom.xml") { out.Kind, out.Label = "java", "Java / Maven" out.Items = []PackSuggestItem{ {Name: "打包", Cmd: "mvn -DskipTests package"}, {Name: "清理打包", Cmd: "mvn clean package -DskipTests"}, } return out } if fileExists(dir, "build.gradle") || fileExists(dir, "build.gradle.kts") { out.Kind, out.Label = "java", "Java / Gradle" out.Items = []PackSuggestItem{ {Name: "构建", Cmd: "gradle build -x test"}, {Name: "bootJar", Cmd: "gradle bootJar"}, } return out } if hasExtInDir(dir, ".csproj") || hasExtInDir(dir, ".sln") { out.Kind, out.Label = "dotnet", ".NET" out.Items = []PackSuggestItem{ {Name: "发布", Cmd: "dotnet publish -c Release"}, {Name: "构建", Cmd: "dotnet build -c Release"}, } return out } if fileExists(dir, "composer.json") { out.Kind, out.Label = "php", "PHP" out.Items = []PackSuggestItem{ {Name: "安装生产依赖", Cmd: "composer install --no-dev -o"}, {Name: "优化自动加载", Cmd: "composer dump-autoload -o"}, } return out } return out } func npmPM(dir string) string { if fileExists(dir, "bun.lockb") || fileExists(dir, "bun.lock") { return "bun" } if fileExists(dir, "pnpm-lock.yaml") { return "pnpm" } if fileExists(dir, "yarn.lock") { return "yarn" } return "npm" } type npmPkgFull struct { Name string `json:"name"` Scripts map[string]string `json:"scripts"` Dependencies map[string]string `json:"dependencies"` DevDependencies map[string]string `json:"devDependencies"` } func readNPMPackage(dir string) npmPkgFull { var pkg npmPkgFull raw, e := os.ReadFile(filepath.Join(dir, "package.json")) if e == nil { _ = json.Unmarshal(raw, &pkg) } return pkg } func pkgHasScript(pkg npmPkgFull, key string) bool { if pkg.Scripts == nil { return false } _, ok := pkg.Scripts[key] return ok } func pkgHasDep(pkg npmPkgFull, names ...string) bool { check := func(m map[string]string) bool { for k := range m { lk := strings.ToLower(k) for _, n := range names { if lk == n || strings.Contains(lk, n) { return true } } } return false } return check(pkg.Dependencies) || check(pkg.DevDependencies) } func isWails3Project(dir string) bool { if fileExists(dir, "build/config.yml") && (fileExists(dir, "Taskfile.yml") || fileExists(dir, "Taskfile.yaml")) { return true } if b, e := os.ReadFile(filepath.Join(dir, "go.mod")); e == nil { s := string(b) if strings.Contains(s, "github.com/wailsapp/wails/v3") { return true } } for _, name := range []string{"Taskfile.yml", "Taskfile.yaml"} { if b, e := os.ReadFile(filepath.Join(dir, name)); e == nil && strings.Contains(string(b), "wails3") { return true } } return false } func hasWailsV2Mod(dir string) bool { b, e := os.ReadFile(filepath.Join(dir, "go.mod")) if e != nil { return false } s := string(b) return strings.Contains(s, "github.com/wailsapp/wails/v2") } func isVbenProject(dir string, pkg npmPkgFull) bool { n := strings.ToLower(pkg.Name) if strings.Contains(n, "vben") { return true } if pkgHasDep(pkg, "@vben/", "vben") { return true } if dirExists(dir, "apps/web-antd") || dirExists(dir, "apps/web-ele") || dirExists(dir, "packages/effects") { return true } return false } func isNextProject(dir string) bool { return fileExists(dir, "next.config.js") || fileExists(dir, "next.config.mjs") || fileExists(dir, "next.config.ts") || dirExists(dir, ".next") } func isNuxtProject(dir string) bool { return fileExists(dir, "nuxt.config.js") || fileExists(dir, "nuxt.config.ts") || fileExists(dir, "nuxt.config.mjs") } func isElectronProject(pkg npmPkgFull) bool { return pkgHasDep(pkg, "electron", "electron-builder", "electron-vite") } func isVueProject(dir string, pkg npmPkgFull) bool { return pkgHasDep(pkg, "vue", "@vitejs/plugin-vue") } func isReactProject(pkg npmPkgFull) bool { return pkgHasDep(pkg, "react", "react-dom", "next") } func packScriptsOr(pkg npmPkgFull, run func(string) string, keys []string, fallback []PackSuggestItem) []PackSuggestItem { items := []PackSuggestItem{} for _, k := range keys { if pkgHasScript(pkg, k) { items = append(items, PackSuggestItem{Name: k, Cmd: run(k)}) } } if len(items) == 0 { return fallback } return items } func collectNPMBuildScripts(pkg npmPkgFull, run func(string) string) []PackSuggestItem { if pkg.Scripts == nil { return nil } prefer := []string{"build", "build:prod", "build:pro", "build:release", "package", "dist", "release"} var items []PackSuggestItem seen := map[string]bool{} for _, k := range prefer { if pkgHasScript(pkg, k) && !seen[k] { seen[k] = true items = append(items, PackSuggestItem{Name: k, Cmd: run(k)}) } } for k := range pkg.Scripts { lk := strings.ToLower(k) if seen[k] { continue } if strings.HasPrefix(lk, "build") || strings.HasPrefix(lk, "package") || strings.HasPrefix(lk, "dist") || strings.HasPrefix(lk, "release") { seen[k] = true items = append(items, PackSuggestItem{Name: k, Cmd: run(k)}) } } if len(items) > 10 { items = items[:10] } return items }