refactor: Update build process to include SUI CompileAsComponent support (dev)

This commit is contained in:
Max 2024-07-05 19:54:26 +08:00
parent 4f3f83523a
commit a4f28368d4
5 changed files with 214 additions and 83 deletions

View file

@ -86,88 +86,16 @@ func (r *Request) Render() (string, int, error) {
if c == nil {
// The page is not cached
message := fmt.Sprintf("[SUI] The page %s is not cached. file=%s DisableCache=%v", r.Request.URL.Path, r.File, r.Request.DisableCache())
go fmt.Println(color.YellowString(message))
go log.Warn(message)
// Read the file
content, err := application.App.Read(r.File)
var status int
var err error
c, status, err = r.MakeCache()
if err != nil {
return "", 404, err
return "", status, err
}
doc, err := core.NewDocument(content)
if err != nil {
return "", 500, err
}
guard := ""
guardRedirect := ""
configText := ""
cacheStore := ""
cacheTime := 0
dateCacheTime := 0
configSel := doc.Find("script[name=config]")
if configSel != nil && configSel.Length() > 0 {
configText = configSel.Text()
configSel.Remove()
var conf core.PageConfig
err := jsoniter.UnmarshalFromString(configText, &conf)
if err != nil {
return "", 500, fmt.Errorf("config error, please re-complie the page %s", err.Error())
}
// Redirect the page (should refector before release)
// guard=cookie-jwt:redirect-url redirect to the url if not authorized
// guard=cookie-jwt return {code: 403, message: "Not Authorized"}
guard = conf.Guard
if strings.Contains(conf.Guard, ":") {
parts := strings.Split(conf.Guard, ":")
guard = parts[0]
guardRedirect = parts[1]
}
// Cache store
cacheStore = conf.CacheStore
cacheTime = conf.Cache
dateCacheTime = conf.DataCache
}
dataText := ""
dataSel := doc.Find("script[name=data]")
if dataSel != nil && dataSel.Length() > 0 {
dataText = dataSel.Text()
dataSel.Remove()
}
globalDataText := ""
globalDataSel := doc.Find("script[name=global]")
if globalDataSel != nil && globalDataSel.Length() > 0 {
globalDataText = globalDataSel.Text()
globalDataSel.Remove()
}
html, err := doc.Html()
if err != nil {
return "", 500, fmt.Errorf("parse error, please re-complie the page %s", err.Error())
}
// Save to The Cache
c = &core.Cache{
Data: dataText,
Global: globalDataText,
HTML: html,
Guard: guard,
GuardRedirect: guardRedirect,
Config: configText,
CacheStore: cacheStore,
CacheTime: time.Duration(cacheTime) * time.Second,
DataCacheTime: time.Duration(dateCacheTime) * time.Second,
}
go core.SetCache(r.File, c)
go log.Trace("[SUI] The page %s is cached file=%s", r.Request.URL.Path, r.File)
}
@ -249,6 +177,90 @@ func (r *Request) Render() (string, int, error) {
return html, 200, nil
}
// MakeCache is the cache for the page API.
func (r *Request) MakeCache() (*core.Cache, int, error) {
// Read the file
content, err := application.App.Read(r.File)
if err != nil {
return nil, 404, err
}
doc, err := core.NewDocument(content)
if err != nil {
return nil, 500, err
}
guard := ""
guardRedirect := ""
configText := ""
cacheStore := ""
cacheTime := 0
dateCacheTime := 0
configSel := doc.Find("script[name=config]")
if configSel != nil && configSel.Length() > 0 {
configText = configSel.Text()
configSel.Remove()
var conf core.PageConfig
err := jsoniter.UnmarshalFromString(configText, &conf)
if err != nil {
return nil, 500, fmt.Errorf("config error, please re-complie the page %s", err.Error())
}
// Redirect the page (should refector before release)
// guard=cookie-jwt:redirect-url redirect to the url if not authorized
// guard=cookie-jwt return {code: 403, message: "Not Authorized"}
guard = conf.Guard
if strings.Contains(conf.Guard, ":") {
parts := strings.Split(conf.Guard, ":")
guard = parts[0]
guardRedirect = parts[1]
}
// Cache store
cacheStore = conf.CacheStore
cacheTime = conf.Cache
dateCacheTime = conf.DataCache
}
dataText := ""
dataSel := doc.Find("script[name=data]")
if dataSel != nil && dataSel.Length() > 0 {
dataText = dataSel.Text()
dataSel.Remove()
}
globalDataText := ""
globalDataSel := doc.Find("script[name=global]")
if globalDataSel != nil && globalDataSel.Length() > 0 {
globalDataText = globalDataSel.Text()
globalDataSel.Remove()
}
html, err := doc.Html()
if err != nil {
return nil, 500, fmt.Errorf("parse error, please re-complie the page %s", err.Error())
}
// Save to The Cache
cache := &core.Cache{
Data: dataText,
Global: globalDataText,
HTML: html,
Guard: guard,
GuardRedirect: guardRedirect,
Config: configText,
CacheStore: cacheStore,
CacheTime: time.Duration(cacheTime) * time.Second,
DataCacheTime: time.Duration(dateCacheTime) * time.Second,
}
go core.SetCache(r.File, cache)
return cache, 200, nil
}
// Guard the page
func (r *Request) Guard(c *core.Cache) (int, error) {

100
sui/api/request_test.go Normal file
View file

@ -0,0 +1,100 @@
package api
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/yao/sui/core"
)
func TestMakeCache(t *testing.T) {
prepare(t)
defer clean()
r := makeRequest("/unit-test/index.sui", t)
c, status, err := r.MakeCache()
if err != nil {
t.Fatal(err)
}
assert.Equal(t, http.StatusOK, status)
assert.Contains(t, c.HTML, "The advanced test cases")
}
func TestRender(t *testing.T) {
prepare(t)
defer clean()
parser, html, data := makeParser("/unit-test/index.sui", t)
result, err := parser.Render(html)
if err != nil {
t.Fatal(err)
}
assert.Contains(t, result, "The advanced test cases")
assert.NotNil(t, data["$global"])
assert.NotNil(t, data["foo"])
assert.NotNil(t, data["items"])
// fmt.Println(result)
}
func makeParser(route string, t *testing.T) (*core.TemplateParser, string, core.Data) {
r := makeRequest(route, t)
c, _, err := r.MakeCache()
if err != nil {
t.Fatal(err)
}
data := r.Request.NewData()
if c.Data != "" {
err = r.Request.ExecStringMerge(data, c.Data)
if err != nil {
t.Fatal(err)
}
}
if c.Global != "" {
global, err := r.Request.ExecString(c.Global)
if err != nil {
t.Fatal(err)
}
data["$global"] = global
}
// Set the page request data
option := core.ParserOption{
Theme: r.Request.Theme,
Locale: r.Request.Locale,
Debug: r.Request.DebugMode(),
DisableCache: r.Request.DisableCache(),
Route: r.Request.URL.Path,
Request: true,
}
// Parse the template
return core.NewTemplateParser(data, &option), c.HTML, data
}
func makeRequest(path string, t *testing.T) *Request {
req, err := http.NewRequest(http.MethodGet, path, nil)
if err != nil {
t.Fatal(err)
}
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = req
r, status, err := NewRequestContext(c)
if err != nil {
t.Fatal(err)
}
if status != http.StatusOK {
t.Fatalf("Status: %d", status)
}
return r
}

View file

@ -31,6 +31,16 @@ func prepare(t *testing.T) {
if err != nil {
t.Fatal(err)
}
advanced, err := core.SUIs["test"].GetTemplate("advanced")
if err != nil {
t.Fatal(err)
}
err = advanced.Build(&core.BuildOption{SSR: true})
if err != nil {
t.Fatal(err)
}
}
func clean() {

View file

@ -62,7 +62,11 @@ func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Docume
doc.Selection.Find("body").AppendHtml("\n" + `<script src="` + script + `"></script>` + "\n")
}
}
doc.Selection.Find("body").AppendHtml(fmt.Sprintf("\n"+`<script type="text/javascript">`+"\n%s\n"+`</script>`+"\n%s\n", strings.Join(ctx.scripts, "\n"), code))
componentScripts := ""
if len(ctx.scripts) > 0 {
componentScripts = fmt.Sprintf("\n"+`<script type="text/javascript" name="components">`+"\n%s\n"+`</script>`+"\n", strings.Join(ctx.scripts, "\n"))
}
doc.Selection.Find("body").AppendHtml(fmt.Sprintf("\n%s\n%s\n", componentScripts, code))
return doc, warnings, nil
}
@ -373,7 +377,7 @@ func (page *Page) BuildScript(ctx *BuildContext, option *BuildOption, namespace
// if the script is a component and not the first import
if option.ComponentName != "" && ctx.components[option.ComponentName] {
ctx.scripts = append(ctx.scripts, instanceCode)
return fmt.Sprintf("<script type=\"text/javascript\">\n%s\n</script>\n", instanceCode), []string{}, nil
return fmt.Sprintf("<script type=\"text/javascript\" name=\"%s\">\n%s\n</script>\n", option.ComponentName, instanceCode), []string{}, nil
}
// TypeScript
@ -402,12 +406,15 @@ func (page *Page) BuildScript(ctx *BuildContext, option *BuildOption, namespace
}
if option.Namespace == "" {
return fmt.Sprintf("<script type=\"text/javascript\">\n%s\n</script>\n", code), scripts, nil
if strings.TrimSpace(string(code)) == "" {
return "", scripts, nil
}
return fmt.Sprintf("<script type=\"text/javascript\" name=\"page\">\n%s\n</script>\n", code), scripts, nil
}
componentCode := fmt.Sprintf("function %s(){\n%s\n}\n%s\n", option.ComponentName, addTabToEachLine(string(code)), instanceCode)
ctx.scripts = append(ctx.scripts, componentCode)
return fmt.Sprintf("<script type=\"text/javascript\">%s</script>\n", componentCode), scripts, nil
return fmt.Sprintf("<script type=\"text/javascript\" name=\"%s\">%s</script>\n", option.ComponentName, componentCode), scripts, nil
}
// JavaScript
@ -434,12 +441,15 @@ func (page *Page) BuildScript(ctx *BuildContext, option *BuildOption, namespace
}
if option.Namespace == "" {
return fmt.Sprintf("<script type=\"text/javascript\">\n%s\n</script>\n", code), scripts, nil
if strings.TrimSpace(string(code)) == "" {
return "", scripts, nil
}
return fmt.Sprintf("<script type=\"text/javascript\" name=\"page\">\n%s\n</script>\n", code), scripts, nil
}
componentCode := fmt.Sprintf("function %s(){\n%s\n}\n%s\n", option.ComponentName, addTabToEachLine(string(code)), instanceCode)
ctx.scripts = append(ctx.scripts, componentCode)
return fmt.Sprintf("<script type=\"text/javascript\">%s</script>\n", componentCode), scripts, nil
return fmt.Sprintf("<script type=\"text/javascript\" name=\"%s\" >%s</script>\n", option.ComponentName, componentCode), scripts, nil
}
func addTabToEachLine(input string, prefix ...string) string {

View file

@ -1 +0,0 @@
package core