Skip to content

Commit 688177b

Browse files
authored
Merge pull request #746 from JiepengTan/pr_cmd_init
Feat: enhance the spx command tool to support running `spx xxx` outside project directories
2 parents b7ac2c0 + de637b3 commit 688177b

5 files changed

Lines changed: 174 additions & 7 deletions

File tree

cmd/gox/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ var (
1616
//go:embed template/.gitignore.txt
1717
gitignoreTxt string
1818

19+
//go:embed template/go.mod.template
20+
gomodtemplate string
21+
1922
//go:embed appname.txt
2023
appName string
2124

@@ -37,6 +40,7 @@ func main() {
3740
cmd.GitignoreTxt = gitignoreTxt
3841
cmd.RunSh = runSh
3942
cmd.MainSh = mainSh
43+
cmd.GoModTemplate = gomodtemplate
4044

4145
// Initialize the Args field if not already initialized
4246
cmd.RunCmd(appName, appName, version, projectFS, "template/project", "project")

cmd/gox/pkg/command/cmd.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
_ "embed"
66
"fmt"
77
"go/build"
8-
"log"
98
"os"
109
"path/filepath"
1110

@@ -47,6 +46,8 @@ type CmdTool struct {
4746
RuntimeTempDir string
4847
RuntimePckPath string
4948
RuntimeCmdPath string
49+
50+
GoModTemplate string
5051
}
5152

5253
// RunCmd executes the specified command with the given parameters
@@ -80,6 +81,11 @@ func (cmd *CmdTool) RunCmd(projectName, fileSuffix, version string, fs embed.FS,
8081
return err
8182
}
8283

84+
// Handle init command early, before setupPaths
85+
if cmd.Args.CmdName == "init" {
86+
return cmd.Init()
87+
}
88+
8389
// Setup paths
8490
err = cmd.setupPaths(dstRelDir)
8591
if err != nil {
@@ -131,12 +137,6 @@ func (cmd *CmdTool) RunCmd(projectName, fileSuffix, version string, fs embed.FS,
131137
return err
132138
}
133139

134-
switch cmd.Args.CmdName {
135-
case "init":
136-
log.Println("Initializing project...")
137-
return nil
138-
}
139-
140140
// Execute the command
141141

142142
// Handle build commands

cmd/gox/pkg/command/env.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,93 @@ func (cmd *CmdTool) setupPaths(dstRelDir string) error {
5656
// PrepareEnv prepares the environment for the command
5757
func (pself *CmdTool) PrepareEnv(fsRelDir, dstDir string) {
5858
util.CopyDir(pself.ProjectFS, fsRelDir, dstDir, false)
59+
60+
// Handle go.mod file adaptively
61+
pself.adaptGoMod()
62+
5963
rawDir, _ := os.Getwd()
6064
os.Chdir(pself.GoDir)
6165
util.RunGolang(nil, "mod", "tidy")
66+
6267
os.Chdir(rawDir)
6368
}
6469

70+
// adaptGoMod adapts the go.mod file for different directory structures
71+
func (pself *CmdTool) adaptGoMod() {
72+
projDir := path.Join(pself.GoDir, "../")
73+
goModPath := path.Join(projDir, "go.mod")
74+
// Check if go.mod exists
75+
if _, err := os.Stat(goModPath); os.IsNotExist(err) {
76+
return
77+
}
78+
79+
// Read go.mod content
80+
content, err := os.ReadFile(goModPath)
81+
if err != nil {
82+
return
83+
}
84+
85+
strContent := string(content)
86+
87+
// Check if we're in a spx directory by looking for spx/gop.mod
88+
currentDir, _ := os.Getwd()
89+
spxPath := pself.findSpxRoot(currentDir)
90+
91+
if spxPath != "" {
92+
// We found spx root, calculate relative path
93+
relPath, err := filepath.Rel(projDir, spxPath)
94+
if err != nil {
95+
relPath = "../../../" // fallback to original
96+
}
97+
98+
// Update the replace directive
99+
oldPattern := `replace github.com/goplus/spx/v2 => ../../../`
100+
newReplace := fmt.Sprintf("replace github.com/goplus/spx/v2 => %s", relPath)
101+
replacedContent := strings.ReplaceAll(strContent, oldPattern, newReplace)
102+
103+
// Write back the modified content
104+
os.WriteFile(goModPath, []byte(replacedContent), 0644)
105+
} else {
106+
// create default go mod file if not exist
107+
pself.createDefaultGoMod(pself.TargetDir, false)
108+
// copy root mod to project dir
109+
util.CopyFile(path.Join(pself.TargetDir, "go.mod"), path.Join(projDir, "go.mod"))
110+
}
111+
}
112+
113+
// createDefaultGoMod ensures gop.mod exists if not in spx directory
114+
func (pself *CmdTool) createDefaultGoMod(dir string, forceWrite bool) {
115+
// Not in spx directory, create gop.mod in target directory
116+
gopModPath := path.Join(dir, "go.mod")
117+
if _, err := os.Stat(gopModPath); os.IsNotExist(err) || forceWrite {
118+
gopModContent := pself.GoModTemplate
119+
os.WriteFile(gopModPath, []byte(gopModContent), 0644)
120+
}
121+
}
122+
123+
// findSpxRoot finds the spx root directory by looking for gop.mod
124+
func (pself *CmdTool) findSpxRoot(startDir string) string {
125+
currentDir := startDir
126+
for {
127+
gopModPath := path.Join(currentDir, "gop.mod")
128+
if _, err := os.Stat(gopModPath); err == nil {
129+
// Check if this gop.mod contains spx project definition
130+
content, err := os.ReadFile(gopModPath)
131+
if err == nil && strings.Contains(string(content), "github.com/goplus/spx/v2") {
132+
return currentDir
133+
}
134+
}
135+
136+
parent := filepath.Dir(currentDir)
137+
if parent == currentDir {
138+
// Reached root directory
139+
break
140+
}
141+
currentDir = parent
142+
}
143+
return ""
144+
}
145+
65146
// SetupEnv sets up the environment for the command
66147
func (pself *CmdTool) SetupEnv(version string, fs embed.FS, fsRelDir string, projectRelPath string) (err error) {
67148
// Update the CmdTool struct fields

cmd/gox/pkg/command/init.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package command
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
)
8+
9+
// Init initializes a new SPX project in the specified directory
10+
func (cmd *CmdTool) Init() error {
11+
// Use the path from command line arguments, default to current directory
12+
targetPath := *cmd.Args.Path
13+
if targetPath == "." {
14+
var err error
15+
targetPath, err = os.Getwd()
16+
if err != nil {
17+
return fmt.Errorf("failed to get current directory: %w", err)
18+
}
19+
} else {
20+
// Convert to absolute path
21+
var err error
22+
targetPath, err = filepath.Abs(targetPath)
23+
if err != nil {
24+
return fmt.Errorf("failed to resolve target path: %w", err)
25+
}
26+
27+
// Create the target directory if it doesn't exist
28+
if err := os.MkdirAll(targetPath, 0755); err != nil {
29+
return fmt.Errorf("failed to create target directory: %w", err)
30+
}
31+
}
32+
33+
fmt.Printf("Initializing SPX project in: %s\n", targetPath)
34+
35+
// Create assets directory
36+
assetsDir := filepath.Join(targetPath, "assets")
37+
if err := os.MkdirAll(assetsDir, 0755); err != nil {
38+
return fmt.Errorf("failed to create assets directory: %w", err)
39+
}
40+
41+
// Create assets/index.json file
42+
indexJsonPath := filepath.Join(assetsDir, "index.json")
43+
indexJsonContent := `
44+
{
45+
"map": {
46+
"width":480,
47+
"height":360
48+
}
49+
}`
50+
if err := os.WriteFile(indexJsonPath, []byte(indexJsonContent), 0644); err != nil {
51+
return fmt.Errorf("failed to create assets/index.json: %w", err)
52+
}
53+
54+
// Create main.spx file
55+
mainSpxPath := filepath.Join(targetPath, "main.spx")
56+
mainSpxContent := `// SPX Project Main File
57+
// This is the entry point for your SPX project
58+
59+
onStart => {
60+
println("Hello, SPX!")
61+
println("Project started successfully!")
62+
}
63+
`
64+
if err := os.WriteFile(mainSpxPath, []byte(mainSpxContent), 0644); err != nil {
65+
return fmt.Errorf("failed to create main.spx: %w", err)
66+
}
67+
68+
cmd.createDefaultGoMod(targetPath, true)
69+
70+
fmt.Println("")
71+
fmt.Println("SPX project initialized successfully!")
72+
fmt.Println("You can now run 'spx run' to start your project.")
73+
74+
return nil
75+
}

cmd/gox/template/go.mod.template

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
module github.com/goplus/spxdemo
3+
4+
go 1.23.0
5+
6+
require github.com/goplus/spx/v2 v2.0.0-pre.1 //gop:class
7+

0 commit comments

Comments
 (0)