Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[add] SUI Template Engine command #511

Merged
merged 1 commit into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/spf13/cobra"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/yao/cmd/studio"
"github.com/yaoapp/yao/cmd/sui"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/pack"
"github.com/yaoapp/yao/share"
Expand Down Expand Up @@ -62,6 +63,7 @@ var langs = map[string]string{
"Error occurred while updating binary: %s": "更新二进制文件时出错: %s",
"🎉Successfully updated to version: %s🎉": "🎉成功更新到版本: %s🎉",
"Print all version information": "显示详细版本信息",
"SUI Template Engine": "SUI 模板引擎命令",
}

// L 多语言切换
Expand Down Expand Up @@ -110,9 +112,26 @@ var studioCmd = &cobra.Command{
},
}

var suiCmd = &cobra.Command{
Use: "sui",
Short: L("SUI Template Engine"),
Long: L("SUI Template Engine"),
Args: cobra.MinimumNArgs(1),
CompletionOptions: cobra.CompletionOptions{
DisableDefaultCmd: true,
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Fprintln(os.Stderr, L("One or more arguments are not correct"), args)
os.Exit(1)
},
}

// 加载命令
func init() {

studioCmd.AddCommand(studio.RunCmd)
suiCmd.AddCommand(sui.WatchCmd)

rootCmd.AddCommand(
versionCmd,
migrateCmd,
Expand All @@ -126,6 +145,7 @@ func init() {
// websocketCmd,
packCmd,
studioCmd,
suiCmd,
upgradeCmd,
)
// rootCmd.SetHelpCommand(helpCmd)
Expand Down
54 changes: 54 additions & 0 deletions cmd/sui/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package sui

import (
"os"
"path/filepath"

"github.com/yaoapp/kun/exception"
"github.com/yaoapp/yao/config"
)

var appPath string
var envFile string

var langs = map[string]string{
"Auto-build when the template file changes": "模板文件变化时自动构建",
"Session Data": "会话数据",
}

// L 多语言切换
func L(words string) string {

var lang = os.Getenv("YAO_LANG")
if lang == "" {
return words
}

if trans, has := langs[words]; has {
return trans
}
return words
}

// Boot 设定配置
func Boot() {
root := config.Conf.Root
if appPath != "" {
r, err := filepath.Abs(appPath)
if err != nil {
exception.New("Root error %s", 500, err.Error()).Throw()
}
root = r
}
if envFile != "" {
config.Conf = config.LoadFrom(envFile)
} else {
config.Conf = config.LoadFrom(filepath.Join(root, ".env"))
}

if config.Conf.Mode == "production" {
config.Production()
} else if config.Conf.Mode == "development" {
config.Development()
}
}
223 changes: 223 additions & 0 deletions cmd/sui/watch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
package sui

import (
"fmt"
"io/fs"
"os"
"os/signal"
"path/filepath"
"strings"
"sync"
"syscall"

"github.com/fatih/color"
"github.com/fsnotify/fsnotify"
"github.com/google/uuid"
jsoniter "github.com/json-iterator/go"
"github.com/spf13/cobra"
"github.com/yaoapp/gou/session"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/engine"
"github.com/yaoapp/yao/sui/core"
)

var data string

var watched sync.Map

// WatchCmd command
var WatchCmd = &cobra.Command{
Use: "watch",
Short: L("Auto-build when the template file changes"),
Long: L("Auto-build when the template file changes"),
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 2 {
fmt.Fprintln(os.Stderr, color.RedString(L("yao cui watch <sui> <template>")))
return
}

Boot()

cfg := config.Conf
err := engine.Load(cfg)
if err != nil {
fmt.Fprintln(os.Stderr, color.RedString(err.Error()))
return
}

id := args[0]
template := args[1]

var sessionData map[string]interface{}
err = jsoniter.UnmarshalFromString(strings.TrimPrefix(data, "::"), &sessionData)
if err != nil {
fmt.Fprintln(os.Stderr, color.RedString(err.Error()))
return
}

sid := uuid.New().String()
if sessionData != nil && len(sessionData) > 0 {
session.Global().ID(sid).SetMany(sessionData)
}

sui, has := core.SUIs[id]
if !has {
fmt.Fprintf(os.Stderr, color.RedString(("the sui " + id + " does not exist")))
return
}
sui.WithSid(sid)

tmpl, err := sui.GetTemplate(template)
if err != nil {
fmt.Fprintln(os.Stderr, color.RedString(err.Error()))
return
}
exitSignal := make(chan os.Signal, 1)
signal.Notify(exitSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
watchDone := make(chan uint8, 1)

// -
root := filepath.Join(cfg.DataRoot, tmpl.GetRoot())
publicRoot, err := sui.PublicRootWithSid(sid)
assetRoot := filepath.Join(publicRoot, "assets")
if err != nil {
fmt.Fprintln(os.Stderr, color.RedString(err.Error()))
return
}

go watch(root, func(event, name string) {
if event == "WRITE" || event == "CREATE" || event == "RENAME" {
// @Todo build single page and sync single asset file to public

fmt.Printf(color.WhiteString("Building... "))
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"))
}
}, watchDone)

fmt.Println(color.WhiteString("-----------------------"))
fmt.Println(color.WhiteString("Public Root: /public%s", publicRoot))
fmt.Println(color.WhiteString(" Template: %s", tmpl.GetRoot()))
fmt.Println(color.WhiteString(" Session: %s", strings.TrimLeft(data, "::")))
fmt.Println(color.WhiteString("-----------------------"))
fmt.Println(color.GreenString("Watching..."))
fmt.Println(color.GreenString("Press Ctrl+C to exit"))

if err != nil {
fmt.Fprintln(os.Stderr, color.RedString(err.Error()))
return
}

select {
case <-exitSignal:
watchDone <- 1
return
}
},
}

func init() {
WatchCmd.PersistentFlags().StringVarP(&data, "data", "d", "::{}", L("Session Data"))
}

func watch(root string, handler func(event string, name string), interrupt chan uint8) error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
defer watcher.Close()
shutdown := make(chan bool, 1)

err = filepath.Walk(root, func(path string, info fs.FileInfo, err error) error {
if info.IsDir() {
if filepath.Base(path) == ".tmp" {
return filepath.SkipDir
}

err = watcher.Add(path)
if err != nil {
return err
}
log.Info("[Watch] Watching: %s", strings.TrimPrefix(path, root))
watched.Store(path, true)
}
return nil
})

if err != nil {
return err
}

go func() {
for {
select {
case <-shutdown:
log.Info("[Watch] handler exit")
return

case event, ok := <-watcher.Events:
if !ok {
interrupt <- 1
return
}

basname := filepath.Base(event.Name)
isdir := true
if strings.Contains(basname, ".") {
isdir = false
}

events := strings.Split(event.Op.String(), "|")
for _, eventType := range events {
// ADD / REMOVE Watching dir
if isdir {
switch eventType {
case "CREATE":
log.Info("[Watch] Watching: %s", strings.TrimPrefix(event.Name, root))
watcher.Add(event.Name)
watched.Store(event.Name, true)
break

case "REMOVE":
log.Info("[Watch] Unwatching: %s", strings.TrimPrefix(event.Name, root))
watcher.Remove(event.Name)
watched.Delete(event.Name)
break
}
continue
}

file := strings.TrimLeft(event.Name, root)
handler(eventType, file)
log.Info("[Watch] %s %s", eventType, file)
}

break

case err, ok := <-watcher.Errors:
if !ok {
interrupt <- 2
return
}
log.Error("[Watch] Error: %s", err.Error())
break
}
}
}()

for {
select {
case code := <-interrupt:
shutdown <- true
log.Info("[Watch] Exit(%d)", code)
fmt.Println(color.YellowString("[Watch] Exit(%d)", code))
return nil
}
}

}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ require (
golang.org/x/mod v0.14.0 // indirect
golang.org/x/oauth2 v0.14.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/tools v0.15.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
Expand Down
1 change: 1 addition & 0 deletions sui/api/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func NewRequestContext(c *gin.Context) (*Request, int, error) {
func (r *Request) Render() (string, int, error) {

c := core.GetCache(r.File)
c = nil // disable cache @todo disable cache on development
if c == nil {
// Read the file
content, err := application.App.Read(r.File)
Expand Down
2 changes: 2 additions & 0 deletions sui/core/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ type ITemplate interface {

Build(option *BuildOption) error
SyncAssets(option *BuildOption) error

GetRoot() string
}

// IPage is the interface for the page
Expand Down
5 changes: 5 additions & 0 deletions sui/storages/local/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ func (tmpl *Template) Assets() []string {
return nil
}

// GetRoot get the root path
func (tmpl *Template) GetRoot() string {
return tmpl.Root
}

// Locales get the global locales
func (tmpl *Template) Locales() []core.SelectOption {

Expand Down