From 9418133b32e8c834e4e7dd725e5dc0345b7053e2 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 25 Jul 2024 10:10:54 +0800 Subject: [PATCH] Refactor SUI core to load backend scripts for templates --- sui/core/cache.go | 2 + sui/core/types.go | 26 ++++++----- sui/storages/local/build.go | 84 +++++++++++++++++++++++++++++++++- sui/storages/local/local.go | 14 ++++++ sui/storages/local/template.go | 39 ++++++++++++++++ 5 files changed, 152 insertions(+), 13 deletions(-) diff --git a/sui/core/cache.go b/sui/core/cache.go index a53f74db..544ee3aa 100644 --- a/sui/core/cache.go +++ b/sui/core/cache.go @@ -4,6 +4,7 @@ import ( "time" jsoniter "github.com/json-iterator/go" + v8 "github.com/yaoapp/gou/runtime/v8" "github.com/yaoapp/gou/store" "github.com/yaoapp/kun/log" ) @@ -20,6 +21,7 @@ type Cache struct { CacheStore string CacheTime time.Duration DataCacheTime time.Duration + Script *v8.Script // the backend script } const ( diff --git a/sui/core/types.go b/sui/core/types.go index 6e21a859..fbc61d9c 100644 --- a/sui/core/types.go +++ b/sui/core/types.go @@ -5,6 +5,7 @@ import ( "regexp" "github.com/PuerkitoBio/goquery" + v8 "github.com/yaoapp/gou/runtime/v8" "golang.org/x/net/html" ) @@ -38,6 +39,7 @@ type Page struct { Path string `json:"-"` Root string `json:"-"` Codes SourceCodes `json:"-"` + Script *v8.Script `json:"-"` // The backend script name.backend.ts / name.backend.js Document []byte `json:"-"` GlobalData []byte `json:"-"` Attrs map[string]string `json:"-"` @@ -166,17 +168,19 @@ 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:"-"` - Scripts *TemplateScirpts `json:"scripts,omitempty"` - Translator string `json:"translator,omitempty"` + 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"` + Translator string `json:"translator,omitempty"` + BuildScript *v8.Script `json:"-"` // __build.backend.ts / __build.backend.js + GlobalScript *v8.Script `json:"-"` // __global.backend.ts / __global.backend.js } // TemplateScirpts is the struct for the template scripts diff --git a/sui/storages/local/build.go b/sui/storages/local/build.go index c9909820..009280bc 100644 --- a/sui/storages/local/build.go +++ b/sui/storages/local/build.go @@ -54,6 +54,12 @@ func (tmpl *Template) Build(option *core.BuildOption) ([]string, error) { option.AssetRoot = filepath.Join(root, "assets") } + // Write the global script + err = tmpl.writeGlobalScript(option.Data) + if err != nil { + return warnings, err + } + // Sync the assets if err = tmpl.SyncAssets(option); err != nil { return warnings, err @@ -192,6 +198,31 @@ func (tmpl *Template) SyncAssetFile(file string, option *core.BuildOption) error return copy(sourceFile, targetFile) } +func (tmpl *Template) writeGlobalScript(data map[string]interface{}) error { + file, source, err := tmpl.backendScriptSource("__global.backend") + if err != nil { + return err + } + + if source == nil { + return nil + } + + name := filepath.Base(file) + ext := filepath.Ext(name) + root, err := tmpl.local.DSL.PublicRoot(data) + if err != nil { + log.Error("WriteGlobalScript: Get the public root error: %s. use %s", err.Error(), tmpl.local.DSL.Public.Root) + root = tmpl.local.DSL.Public.Root + } + target := filepath.Join(application.App.Root(), "public", root, fmt.Sprintf("__global%s", ext)) + dir := filepath.Dir(target) + if exist, _ := os.Stat(dir); exist == nil { + os.MkdirAll(dir, os.ModePerm) + } + return os.WriteFile(target, source, 0644) +} + // SyncAssets sync the assets func (tmpl *Template) SyncAssets(option *core.BuildOption) error { @@ -333,6 +364,12 @@ func (page *Page) Build(globalCtx *core.GlobalBuildContext, option *core.BuildOp return warnings, fmt.Errorf("Write the page %s error: %s", page.Route, err.Error()) } + // Save the backend script file + err = page.writeBackendScript(option.Data) + if err != nil { + return warnings, fmt.Errorf("Write the backend script file error: %s", err.Error()) + } + // Save the locale files err = page.writeLocaleFiles(ctx, option.Data) if err != nil { @@ -604,6 +641,51 @@ func (page *Page) writeLocaleFiles(ctx *core.BuildContext, data map[string]inter return nil } +func (page *Page) backendScriptSource() (string, []byte, error) { + backendFile := filepath.Join(page.Path, fmt.Sprintf("%s.backend.ts", page.Name)) + if exist, _ := page.tmpl.local.fs.Exists(backendFile); !exist { + backendFile = filepath.Join(page.Path, fmt.Sprintf("%s.backend.js", page.Name)) + } + + if exist, _ := page.tmpl.local.fs.Exists(backendFile); !exist { + return "", nil, nil + } + + source, err := page.tmpl.local.fs.ReadFile(backendFile) + if err != nil { + return "", nil, err + } + + return backendFile, source, nil +} + +func (page *Page) writeBackendScript(data map[string]interface{}) error { + + file, source, err := page.backendScriptSource() + if err != nil { + return err + } + + if source == nil { + return nil + } + + ext := filepath.Ext(file) + scriptFile := fmt.Sprintf("%s%s", page.publicFile(data), ext) + scriptFileAbs := filepath.Join(application.App.Root(), scriptFile) + dir := filepath.Dir(scriptFileAbs) + if exist, _ := os.Stat(dir); exist == nil { + os.MkdirAll(dir, os.ModePerm) + } + + err = os.WriteFile(scriptFileAbs, []byte(source), 0644) + if err != nil { + return err + } + core.RemoveCache(scriptFile) + return nil +} + // writeHTMLTo write the html to file func (page *Page) writeHTML(html []byte, data map[string]interface{}) error { htmlFile := fmt.Sprintf("%s.sui", page.publicFile(data)) @@ -618,7 +700,6 @@ func (page *Page) writeHTML(html []byte, data map[string]interface{}) error { } core.RemoveCache(htmlFile) - log.Trace("The page %s is removed", htmlFile) return nil } @@ -635,6 +716,5 @@ func (page *Page) writeJitHTML(html []byte, data map[string]interface{}) error { return err } core.RemoveCache(htmlFile) - log.Trace("The page %s is removed", htmlFile) return nil } diff --git a/sui/storages/local/local.go b/sui/storages/local/local.go index 7ff8c816..7b69a9d0 100644 --- a/sui/storages/local/local.go +++ b/sui/storages/local/local.go @@ -7,6 +7,7 @@ import ( "strings" jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/gou/application" "github.com/yaoapp/gou/fs" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/sui/core" @@ -156,6 +157,12 @@ func (local *Local) getTemplate(id string, path string) (*Template, error) { tmpl.GlobalData = dataBytes } + // load the __build.backend.ts / __build.backend.js + err := tmpl.loadBuildScript() + if err != nil { + return nil, err + } + return &tmpl, nil } @@ -163,3 +170,10 @@ func (local *Local) getTemplate(id string, path string) (*Template, error) { func (local *Local) getTemplateID(path string) string { return filepath.Base(path) } + +// AppRoot get the app root +func (local *Local) AppRoot() string { + approot := application.App.Root() + localroot := local.fs.Root() + return strings.TrimPrefix(localroot, approot) +} diff --git a/sui/storages/local/template.go b/sui/storages/local/template.go index 6928b6f7..5d2ef00f 100644 --- a/sui/storages/local/template.go +++ b/sui/storages/local/template.go @@ -12,6 +12,7 @@ import ( "github.com/google/uuid" "github.com/yaoapp/gou/process" + v8 "github.com/yaoapp/gou/runtime/v8" "github.com/yaoapp/yao/sui/core" "golang.org/x/text/language" ) @@ -85,6 +86,44 @@ func (tmpl *Template) Reload() error { return nil } +// LoadBuildScript load the build script +func (tmpl *Template) loadBuildScript() error { + file, source, err := tmpl.backendScriptSource("__build.backend") + if err != nil { + return err + } + + if file == "" { + return nil + } + + approot := tmpl.local.AppRoot() + file = filepath.Join(approot, file) + script, err := v8.MakeScript(source, file, 5*time.Second) + if err != nil { + return err + } + tmpl.BuildScript = script + return nil +} + +func (tmpl *Template) backendScriptSource(name string) (string, []byte, error) { + path := filepath.Join(tmpl.Root, fmt.Sprintf("%s.ts", name)) + if !tmpl.local.fs.IsFile(path) { + path = filepath.Join(tmpl.Root, fmt.Sprintf("%s.js", name)) + } + + if !tmpl.local.fs.IsFile(path) { + return "", nil, nil + } + + content, err := tmpl.local.fs.ReadFile(path) + if err != nil { + return "", nil, err + } + return path, content, nil +} + // ExecBuildCompleteScripts execute the build complete scripts func (tmpl *Template) ExecBuildCompleteScripts() []core.TemplateScirptResult { if tmpl.Scripts == nil || len(tmpl.Scripts.BuildComplete) == 0 {