new.go
100 lines1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package commands
import (
"flag"
"fmt"
"log"
"os"
"strings"
"congo.gg/cmd/internal"
)
const newUsage = `Add a new app to an existing Congo project
Usage:
congo new <name> [flags]
Flags:
--frontend Include React islands
--no-database Exclude database models
Creates a new app directory with controllers, models, views, and main.go.
Must be run from inside a Congo project (directory with go.mod).
Examples:
congo new api
congo new admin --frontend
congo new worker --no-database
`
func New() {
fs := flag.NewFlagSet("new", flag.ExitOnError)
fs.Usage = func() { fmt.Print(newUsage) }
withFrontend := fs.Bool("frontend", false, "Include React islands")
noDatabase := fs.Bool("no-database", false, "Exclude database models")
fs.Parse(os.Args[2:])
if fs.NArg() < 1 {
fs.Usage()
os.Exit(1)
}
name := fs.Arg(0)
if !validName.MatchString(name) {
log.Fatalf("invalid app name %q (lowercase letters, numbers, hyphens, underscores)", name)
}
// Must be in a Congo project (go.mod exists).
modData, err := os.ReadFile("go.mod")
if err != nil {
log.Fatal("go.mod not found. Run 'congo init <project>' first to create a project.")
}
// Parse module path from go.mod.
module := parseModulePath(modData)
if module == "" {
log.Fatal("could not parse module path from go.mod")
}
if _, err := os.Stat(name); err == nil {
log.Fatalf("directory %q already exists", name)
}
// Check that internal/ exists (framework already extracted).
if _, err := os.Stat("internal"); err != nil {
log.Fatal("internal/ not found. Run 'congo init <project>' first.")
}
fmt.Printf("Creating app %s...\n", name)
data := scaffold.Data{
Name: name,
Module: module,
Dir: name,
WithFrontend: *withFrontend,
WithDatabase: !*noDatabase,
}
if err := scaffold.RenderApp(name, data); err != nil {
log.Fatalf("render app: %v", err)
}
fmt.Printf("\nDone! App created at ./%s\n\n", name)
fmt.Println("Next steps:")
fmt.Printf(" go run ./%s\n", name)
fmt.Println()
}
// parseModulePath extracts the module path from go.mod content.
func parseModulePath(data []byte) string {
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "module ") {
return strings.TrimSpace(strings.TrimPrefix(line, "module "))
}
}
return ""
}