diff --git a/cmd/sui/build.go b/cmd/sui/build.go index b89384061..914d7a981 100644 --- a/cmd/sui/build.go +++ b/cmd/sui/build.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/fatih/color" "github.com/google/uuid" @@ -78,12 +79,15 @@ var BuildCmd = &cobra.Command{ fmt.Println(color.WhiteString(" Session: %s", strings.TrimLeft(data, "::"))) fmt.Println(color.WhiteString("-----------------------")) - err = tmpl.Build(&core.BuildOption{SSR: true, AssetRoot: assetRoot}) + // Timecost + start := time.Now() + err = tmpl.Build(&core.BuildOption{SSR: true, AssetRoot: assetRoot, ExecScripts: true}) if err != nil { fmt.Fprintln(os.Stderr, color.RedString(err.Error())) return } - - fmt.Print(color.GreenString("Build Success\n")) + end := time.Now() + timecost := end.Sub(start).Truncate(time.Millisecond) + fmt.Println(color.GreenString("Build success in %s", timecost)) }, } diff --git a/cmd/sui/watch.go b/cmd/sui/watch.go index c41d3b4f6..b35021479 100644 --- a/cmd/sui/watch.go +++ b/cmd/sui/watch.go @@ -9,6 +9,7 @@ import ( "strings" "sync" "syscall" + "time" "github.com/fatih/color" "github.com/fsnotify/fsnotify" @@ -97,12 +98,16 @@ var WatchCmd = &cobra.Command{ return } + // Timecost + start := time.Now() err = tmpl.Build(&core.BuildOption{SSR: true, AssetRoot: assetRoot}) if err != nil { fmt.Fprint(os.Stderr, color.RedString(fmt.Sprintf("Failed: %s\n", err.Error()))) return } - fmt.Print(color.GreenString("Success\n")) + end := time.Now() + timecost := end.Sub(start).Truncate(time.Millisecond) + fmt.Printf(color.GreenString("Success (%s)\n"), timecost.String()) } }, watchDone) diff --git a/sui/core/interfaces.go b/sui/core/interfaces.go index 10611e306..1f265e5d6 100644 --- a/sui/core/interfaces.go +++ b/sui/core/interfaces.go @@ -61,8 +61,9 @@ type ITemplate interface { Build(option *BuildOption) error SyncAssets(option *BuildOption) error SyncAssetFile(file string, option *BuildOption) error - GetRoot() string + + ExecBuildScripts() []TemplateScirptResult } // IPage is the interface for the page diff --git a/sui/core/types.go b/sui/core/types.go index 37e00a06d..d83a6f4c2 100644 --- a/sui/core/types.go +++ b/sui/core/types.go @@ -141,15 +141,35 @@ type LayoutItem struct { // Template is the struct for the template type Template struct { - Version int `json:"version"` // Yao Builder version - ID string `json:"id"` - Name string `json:"name"` - Descrption string `json:"description"` - Screenshots []string `json:"screenshots"` - Themes []SelectOption `json:"themes"` - Locales []SelectOption `json:"locales"` - Document []byte `json:"-"` - GlobalData []byte `json:"-"` + Version int `json:"version"` // Yao Builder version + ID string `json:"id"` + Name string `json:"name"` + Descrption string `json:"description"` + Screenshots []string `json:"screenshots"` + Themes []SelectOption `json:"themes"` + Locales []SelectOption `json:"locales"` + Document []byte `json:"-"` + GlobalData []byte `json:"-"` + Scripts *TemplateScirpts `json:"scripts,omitempty"` +} + +// TemplateScirpts is the struct for the template scripts +type TemplateScirpts struct { + Build []*TemplateScript `json:"build,omitempty"` // Run before build +} + +// TemplateScript is the struct for the template script +type TemplateScript struct { + Type string `json:"type"` + Content string `json:"content"` +} + +// TemplateScirptResult is the struct for the template script result +type TemplateScirptResult struct { + Message string `json:"message,omitempty"` + Error error `json:"error,omitempty"` + Pid int `json:"pid,omitempty"` + Script *TemplateScript `json:"script,omitempty"` } // Theme is the struct for the theme @@ -210,6 +230,7 @@ type BuildOption struct { ComponentName string `json:"component_name,omitempty"` ScriptMinify bool `json:"scriptminify,omitempty"` StyleMinify bool `json:"styleminify,omitempty"` + ExecScripts bool `json:"exec_scripts,omitempty"` } // Request is the struct for the request diff --git a/sui/storages/local/build.go b/sui/storages/local/build.go index bece21523..e271c37d2 100644 --- a/sui/storages/local/build.go +++ b/sui/storages/local/build.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/hashicorp/go-multierror" "github.com/yaoapp/gou/application" @@ -16,6 +17,20 @@ import ( func (tmpl *Template) Build(option *core.BuildOption) error { var err error + // Execute the build hook + if option.ExecScripts { + res := tmpl.ExecBuildScripts() + scriptsErrorMessages := []string{} + for _, r := range res { + if r.Error != nil { + scriptsErrorMessages = append(scriptsErrorMessages, fmt.Sprintf("%s: %s", r.Script.Content, r.Error.Error())) + } + } + if len(scriptsErrorMessages) > 0 { + return fmt.Errorf("Build scripts error: %s", strings.Join(scriptsErrorMessages, ";\n")) + } + } + root, err := tmpl.local.DSL.PublicRoot(option.Data) if err != nil { log.Error("SyncAssets: Get the public root error: %s. use %s", err.Error(), tmpl.local.DSL.Public.Root) diff --git a/sui/storages/local/template.go b/sui/storages/local/template.go index 398cbfc40..37fb6bf24 100644 --- a/sui/storages/local/template.go +++ b/sui/storages/local/template.go @@ -5,6 +5,7 @@ import ( "io" "net/url" "os" + "os/exec" "path/filepath" "strings" "time" @@ -24,6 +25,42 @@ func (tmpl *Template) GetRoot() string { return tmpl.Root } +// ExecBuildScripts execute the build scripts +func (tmpl *Template) ExecBuildScripts() []core.TemplateScirptResult { + if tmpl.Scripts == nil || len(tmpl.Scripts.Build) == 0 { + return nil + } + results := []core.TemplateScirptResult{} + for _, script := range tmpl.Scripts.Build { + switch script.Type { + case "command": + results = append(results, tmpl.execCommand(script)) + } + } + return results +} + +func (tmpl *Template) execCommand(script *core.TemplateScript) core.TemplateScirptResult { + result := core.TemplateScirptResult{Script: script, Message: "", Error: nil} + + // Set the current working directory to the template root + root := filepath.Join(tmpl.local.fs.Root(), tmpl.Root) + + // Parse the command + cmd := strings.Split(script.Content, " ") + if len(cmd) == 0 { + result.Error = fmt.Errorf("Command is empty") + return result + } + + execCmd := exec.Command(cmd[0], cmd[1:]...) + execCmd.Dir = root + output, err := execCmd.CombinedOutput() + result.Error = err + result.Message = string(output) + return result +} + // Locales get the global locales func (tmpl *Template) Locales() []core.SelectOption { if tmpl.locales != nil {