Skip to content

Instantly share code, notes, and snippets.

@Quorafind
Created October 30, 2024 14:57
Show Gist options
  • Save Quorafind/c1c373b0515cb261b83145f39c77a78d to your computer and use it in GitHub Desktop.
Save Quorafind/c1c373b0515cb261b83145f39c77a78d to your computer and use it in GitHub Desktop.
Duplicate any templates
{
"templates": [
{
"name": "Vue3 + TypeScript",
"path": "./templates/vue3-ts",
"description": "Vue 3 项目模板,使用 TypeScript",
"version": "1.0.0"
},
{
"name": "React + TypeScript",
"path": "./templates/react-ts",
"description": "React 项目模板,使用 TypeScript",
"version": "1.0.0"
},
{
"name": "Node.js",
"path": "./templates/nodejs",
"description": "Node.js 后端项目模板",
"version": "1.0.0"
}
]
}
package main
import (
"bufio"
"flag"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"encoding/json"
)
// 模板配置
type Template struct {
Name string `json:"name"`
Path string `json:"path"`
Description string `json:"description"`
Version string `json:"version"`
}
type Config struct {
Templates []Template `json:"templates"`
}
var configPath string // 全局变量,用于存储配置文件路径
// 添加加载配置函数
func loadConfig() (*Config, error) {
configFile, err := os.ReadFile(configPath)
if err != nil {
return nil, fmt.Errorf("读取配置文件失败: %v", err)
}
var config Config
if err := json.Unmarshal(configFile, &config); err != nil {
return nil, fmt.Errorf("解析配置文件失败: %v", err)
}
return &config, nil
}
// getTemplateChoice 获取用户选择的模板
func getTemplateChoice(reader *bufio.Reader, templates []Template) (Template, error) {
fmt.Println("可用的项目模板:")
for i, tmpl := range templates {
fmt.Printf("[%d] %s (%s)\n", i+1, tmpl.Name, tmpl.Description)
}
fmt.Print("请选择模板 (输入数字,直接回车使用第一个模板): ")
input, err := reader.ReadString('\n')
if err != nil {
return Template{}, fmt.Errorf("读取输入失败: %v", err)
}
input = strings.TrimSpace(input)
if input == "" {
return templates[0], nil
}
choice, err := validateTemplateChoice(input, len(templates))
if err != nil {
return Template{}, err
}
return templates[choice-1], nil
}
// validateTemplateChoice 验证模板选择是否有效
func validateTemplateChoice(input string, maxChoice int) (int, error) {
var choice int
if _, err := fmt.Sscanf(input, "%d", &choice); err != nil {
return 0, fmt.Errorf("请输入有效的数字")
}
if choice < 1 || choice > maxChoice {
return 0, fmt.Errorf("无效的选择,请输入 1 到 %d 之间的数字", maxChoice)
}
return choice, nil
}
// getProjectName 获取项目名称
func getProjectName(reader *bufio.Reader) (string, error) {
fmt.Print("请输入项目名称 (直接回车使用默认名称): ")
projectName, err := reader.ReadString('\n')
if err != nil {
return "", fmt.Errorf("读取输入失败: %v", err)
}
projectName = strings.TrimSpace(projectName)
if projectName == "" {
now := time.Now()
return fmt.Sprintf("新建项目-%s", now.Format("20060102150405")), nil
}
return projectName, nil
}
// createProject 创建新项目
func createProject(templatePath, targetPath string) error {
// 检查目标路径是否已存在
if _, err := os.Stat(targetPath); err == nil {
return fmt.Errorf("目标路径 '%s' 已存在", targetPath)
}
// 复制模板文件夹
if err := copyDir(templatePath, targetPath); err != nil {
return fmt.Errorf("复制模板失败: %v", err)
}
// 创建或追加TODO.md
todoPath := filepath.Join(targetPath, "TODO.md")
todoContent := "- [ ] 完成编辑\n"
if err := appendToFile(todoPath, todoContent); err != nil {
return fmt.Errorf("添加TODO失败: %v", err)
}
return nil
}
// openInVSCode 在VSCode中打开项目
func openInVSCode(targetPath string) error {
cmd := exec.Command("code", ".")
cmd.Dir = targetPath
if err := cmd.Run(); err != nil {
return fmt.Errorf("打开VSCode失败: %v", err)
}
return nil
}
func main() {
// 命令行参数
var projectName string
var templateName string
var targetDir string
flag.StringVar(&projectName, "name", "", "项目名称")
flag.StringVar(&projectName, "n", "", "项目名称")
flag.StringVar(&templateName, "template", "", "模板名称")
flag.StringVar(&templateName, "t", "", "模板名称")
flag.StringVar(&targetDir, "dir", "", "目标目录")
flag.StringVar(&targetDir, "d", "", "目标目录")
flag.StringVar(&configPath, "config", "config.json", "配置文件路径")
flag.StringVar(&configPath, "c", "config.json", "配置文件路径")
flag.Parse()
// 如果没有指定目标目录,使用当前目录
if targetDir == "" {
var err error
targetDir, err = os.Getwd()
if err != nil {
fmt.Printf("获取当前目录失败: %v\n", err)
return
}
}
// 加载配置
config, err := loadConfig()
if err != nil {
fmt.Println(err)
return
}
reader := bufio.NewReader(os.Stdin)
var selectedTemplate Template
// 获取模板名称
if templateName == "" {
var err error
selectedTemplate, err = getTemplateChoice(reader, config.Templates)
if err != nil {
fmt.Println(err)
return
}
} else {
// 根据命令行参数查找模板
found := false
for _, tmpl := range config.Templates {
if tmpl.Name == templateName {
selectedTemplate = tmpl
found = true
break
}
}
if !found {
fmt.Printf("错误: 模板 '%s' 不存在\n", templateName)
return
}
}
// 获取项目名称
if projectName == "" {
var err error
projectName, err = getProjectName(reader)
if err != nil {
fmt.Println(err)
return
}
}
targetPath := filepath.Join(targetDir, projectName)
// 创建项目
if err := createProject(selectedTemplate.Path, targetPath); err != nil {
fmt.Println(err)
return
}
fmt.Printf("项目创建成功: %s\n", targetPath)
// 在VSCode中打开项目
if err := openInVSCode(targetPath); err != nil {
fmt.Println(err)
return
}
}
// copyDir 递归复制目录
func copyDir(src string, dst string) error {
// 创建目标目录
err := os.MkdirAll(dst, 0755)
if err != nil {
return err
}
// 读取源目录
entries, err := os.ReadDir(src)
if err != nil {
return err
}
for _, entry := range entries {
sourcePath := filepath.Join(src, entry.Name())
destPath := filepath.Join(dst, entry.Name())
if entry.IsDir() {
// 递归复制子目录
err = copyDir(sourcePath, destPath)
if err != nil {
return err
}
} else {
// 复制文件
err = copyFile(sourcePath, destPath)
if err != nil {
return err
}
}
}
return nil
}
// copyFile 复制单个文件
func copyFile(src, dst string) error {
sourceFile, err := os.Open(src)
if err != nil {
return err
}
defer sourceFile.Close()
destFile, err := os.Create(dst)
if err != nil {
return err
}
defer destFile.Close()
_, err = destFile.ReadFrom(sourceFile)
return err
}
// appendToFile 追加内容到文件
func appendToFile(filepath string, content string) error {
f, err := os.OpenFile(filepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()
_, err = f.WriteString(content)
return err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment