From 30bd043202fdd6a083012534ad85675e0926cffe Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Chaudhary Date: Sun, 17 Mar 2024 19:06:07 +0545 Subject: [PATCH 1/3] feat: remove unused statements & show error on generating module when required field is not provide --- cmd/create_project.go | 8 +++++++- cmd/new_module.go | 34 +++++++++++++++++++++++++++------- cmd/root.go | 10 ++-------- cmd/run_project.go | 4 +--- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/cmd/create_project.go b/cmd/create_project.go index 9e3f9ac..bc9db94 100644 --- a/cmd/create_project.go +++ b/cmd/create_project.go @@ -19,6 +19,12 @@ var newProjectCmd = &cobra.Command{ Run: createProject, } +func setupFlagsForNewProject(cmd *cobra.Command) { + cmd.Flags().StringP("mod", "m", "", "module name") + cmd.Flags().StringP("dir", "d", "", "target directory") + cmd.Flags().StringP("version", "v", "", "version support: Default: 1.20") +} + func createProject(cmd *cobra.Command, args []string) { var projectName string var projectModuleName string @@ -79,7 +85,7 @@ func createProject(cmd *cobra.Command, args []string) { return } if projectModuleName == "" { - color.Redln("Error: module name is required") + color.Redln("Error: golang module name is required") return } diff --git a/cmd/new_module.go b/cmd/new_module.go index 7c4057d..cc43917 100644 --- a/cmd/new_module.go +++ b/cmd/new_module.go @@ -7,19 +7,33 @@ import ( "github.com/gookit/color" "github.com/mukezhz/geng/pkg/constant" + "github.com/mukezhz/geng/pkg/model" "github.com/mukezhz/geng/pkg/terminal" "github.com/mukezhz/geng/pkg/utility" "github.com/spf13/cobra" ) var newModuleCmd = &cobra.Command{ - Use: "gen module [name]", + Use: "gen mod [name]", Short: "Create a new domain", - Args: cobra.MaximumNArgs(2), - Run: createModule, + Long: ` +Create a new module|service|middleware in the project. +Example: + geng gen mod [name] + geng gen srv [name] + geng gen mid [name] + +Default: + geng gen -> geng gen mod + `, + Args: cobra.MaximumNArgs(2), + Run: generate, } -func createModule(_ *cobra.Command, args []string) { +func generate(_ *cobra.Command, args []string) { + if len(args) == 0 { + args = append(args, "module") + } projectModule, err := utility.GetModuleNameFromGoModFile() if err != nil { fmt.Println("Error finding Module name from go.mod:", err) @@ -35,6 +49,12 @@ func createModule(_ *cobra.Command, args []string) { fmt.Println("Error finding Git root:", err) return } + // Define the directory structure + generateModule(projectPath, args, projectModule) + +} + +func generateModule(projectPath string, args []string, projectModule model.GoMod) { mainModulePath := filepath.Join(projectPath, "domain", "module.go") var moduleName string if len(args) == 1 { @@ -50,7 +70,7 @@ func createModule(_ *cobra.Command, args []string) { } if q.Input.Exited() { color.Redln("exited without completing...") - return + } } } else { @@ -62,11 +82,10 @@ func createModule(_ *cobra.Command, args []string) { } data := utility.GetModuleDataFromModuleName(moduleName, projectModule.Module, projectModule.GoVersion) - // Define the directory structure targetRoot := filepath.Join(".", "domain", data.PackageName) templatePath := filepath.Join(".", "templates", "wesionary", "module") - err = utility.GenerateFiles(templatesFS, templatePath, targetRoot, data) + err := utility.GenerateFiles(templatesFS, templatePath, targetRoot, data) if err != nil { color.Redln("Error: generate file", err) return @@ -76,4 +95,5 @@ func createModule(_ *cobra.Command, args []string) { utility.WriteContentToPath(mainModulePath, updatedCode) utility.PrintColorizeModuleDetail(data) + } diff --git a/cmd/root.go b/cmd/root.go index 4b778ba..5737dd1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -7,16 +7,10 @@ import ( ) var ( - // Used for flags. - cfgFile string - userLicense string - rootCmd = &cobra.Command{ - Use: "cobra-cli", + Use: "geng", Short: "A generator for Cobra based Applications", - Long: `Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, + Long: `geng is a CLI library for Go that empowers applications.`, } templatesFS embed.FS ) diff --git a/cmd/run_project.go b/cmd/run_project.go index d17537f..e5a88cf 100644 --- a/cmd/run_project.go +++ b/cmd/run_project.go @@ -22,9 +22,7 @@ func runProject(_ *cobra.Command, args []string) { } func init() { - newProjectCmd.Flags().StringP("mod", "m", "", "features name") - newProjectCmd.Flags().StringP("dir", "d", "", "target directory") - newProjectCmd.Flags().StringP("version", "v", "", "version support") + setupFlagsForNewProject(newProjectCmd) rootCmd.AddCommand(newModuleCmd) rootCmd.AddCommand(newProjectCmd) rootCmd.AddCommand(runProjectCmd) From c84503fe283ed02368db0dbc236f2f0a866ff422 Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Chaudhary Date: Sun, 14 Apr 2024 10:18:57 +0545 Subject: [PATCH 2/3] feat: add feature to generate the fx config by reading the comment --- cmd/generate_fx_module.go | 23 +++++++ cmd/run_project.go | 1 + pkg/utility/fx_generator.go | 134 ++++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 cmd/generate_fx_module.go create mode 100644 pkg/utility/fx_generator.go diff --git a/cmd/generate_fx_module.go b/cmd/generate_fx_module.go new file mode 100644 index 0000000..ec119c3 --- /dev/null +++ b/cmd/generate_fx_module.go @@ -0,0 +1,23 @@ +package cmd + +import ( + "github.com/mukezhz/geng/pkg/utility" + "github.com/spf13/cobra" +) + +var generateFxCmd = &cobra.Command{ + Use: "fx", + Short: "Generate the fx configuration file for the project", + Long: ` +Generate a module by reading comments from the source code. +Example: + geng fx + + `, + Args: cobra.MaximumNArgs(2), + Run: generateFx, +} + +func generateFx(_ *cobra.Command, _ []string) { + utility.GenerateFxModule() +} diff --git a/cmd/run_project.go b/cmd/run_project.go index e5a88cf..524bbd2 100644 --- a/cmd/run_project.go +++ b/cmd/run_project.go @@ -28,4 +28,5 @@ func init() { rootCmd.AddCommand(runProjectCmd) rootCmd.AddCommand(addInfrastructureCmd) rootCmd.AddCommand(addServiceCmd) + rootCmd.AddCommand(generateFxCmd) } diff --git a/pkg/utility/fx_generator.go b/pkg/utility/fx_generator.go new file mode 100644 index 0000000..87c20e5 --- /dev/null +++ b/pkg/utility/fx_generator.go @@ -0,0 +1,134 @@ +package utility + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "path/filepath" + "strings" + "text/template" + + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +func GenerateFxModule() { + fset := token.NewFileSet() + root := "./" + moduleMap := make(map[string][]string) + + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + if !strings.HasSuffix(info.Name(), ".go") { + return nil + } + + file, err := parser.ParseFile(fset, path, nil, parser.ParseComments) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to parse file %s: %v\n", path, err) + return err + } + + processedFile := processFile(file) + if len(processedFile) != 0 { + moduleMap[filepath.Dir(path)] = append(moduleMap[filepath.Dir(path)], processedFile...) + } + + return nil + }) + generateFile(moduleMap) + + if err != nil { + fmt.Fprintf(os.Stderr, "Error walking through the directory: %v\n", err) + os.Exit(1) + } +} + +func processFile(file *ast.File) []string { + providers := make([]string, 0) + ast.Inspect(file, func(n ast.Node) bool { + fn, ok := n.(*ast.FuncDecl) + if !ok || fn.Doc == nil { + return true + } + + var provide bool + var asInterface string + + for _, comment := range fn.Doc.List { + if strings.Contains(comment.Text, "@fxProvide") { + provide = true + } + if strings.HasPrefix(comment.Text, "// @fxAs ") { + parts := strings.SplitN(comment.Text, " ", 3) + if len(parts) > 2 { + asInterface = strings.TrimSpace(parts[2]) + } + } + } + + if provide { + if asInterface != "" { + providers = append(providers, fmt.Sprintf("fx.Provide(fx.Annotate(%s, fx.As(new(%s)))),", fn.Name.Name, asInterface)) + } else { + providers = append(providers, fmt.Sprintf("fx.Provide(%s),", fn.Name.Name)) + } + } + return true + }) + return providers +} + +type Module struct { + PackageName string + ModuleName string + Name string + Providers []string +} + +const templateText = `package {{.PackageName}} + +import "go.uber.org/fx" + +// This file is generated by geng. DO NOT EDIT. + +var {{.ModuleName}} = fx.Module("{{.Name}}", +{{- range .Providers}} + {{.}} +{{- end}} +) +` + +func generateFile(dependencies map[string][]string) { + tmpl, err := template.New("module").Parse(templateText) + if err != nil { + panic(err) + } + + for k, v := range dependencies { + module := Module{ + PackageName: filepath.Base(k), + Name: filepath.Base(k), + ModuleName: cases.Title(language.English).String(filepath.Base(k)), + Providers: v, + } + filePath := filepath.Join(k, "module.go") + file, err := os.Create(filePath) + if err != nil { + panic(err) + } + defer file.Close() + + err = tmpl.Execute(file, module) + if err != nil { + panic(err) + } + } +} From 9f84b1581879c86f0b95c5d9cc6665495b3f309c Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Chaudhary Date: Sun, 14 Apr 2024 10:27:04 +0545 Subject: [PATCH 3/3] refactor: add message after code run --- pkg/utility/fx_generator.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/utility/fx_generator.go b/pkg/utility/fx_generator.go index 87c20e5..4a07821 100644 --- a/pkg/utility/fx_generator.go +++ b/pkg/utility/fx_generator.go @@ -98,6 +98,8 @@ const templateText = `package {{.PackageName}} import "go.uber.org/fx" // This file is generated by geng. DO NOT EDIT. +// File generated at: {{.Timestamp}} +// File will be overwritten when running geng again. var {{.ModuleName}} = fx.Module("{{.Name}}", {{- range .Providers}} @@ -131,4 +133,6 @@ func generateFile(dependencies map[string][]string) { panic(err) } } + count := len(dependencies) + fmt.Printf("Generated %d module files\n", count) }