optimize the SUI compiler & add http server gzip support

This commit is contained in:
Max 2024-07-07 15:29:50 +08:00
parent 536621d72e
commit e4c8eeb180
11 changed files with 169 additions and 44 deletions

View file

@ -81,13 +81,25 @@ var BuildCmd = &cobra.Command{
// Timecost // Timecost
start := time.Now() start := time.Now()
err = tmpl.Build(&core.BuildOption{SSR: true, AssetRoot: assetRoot, ExecScripts: true}) minify := true
mode := "production"
if debug {
minify = false
mode = "development"
}
err = tmpl.Build(&core.BuildOption{SSR: true, AssetRoot: assetRoot, ExecScripts: true, ScriptMinify: minify, StyleMinify: minify})
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, color.RedString(err.Error())) fmt.Fprintln(os.Stderr, color.RedString(err.Error()))
return return
} }
end := time.Now() end := time.Now()
timecost := end.Sub(start).Truncate(time.Millisecond) timecost := end.Sub(start).Truncate(time.Millisecond)
fmt.Println(color.GreenString("Build success in %s", timecost)) if debug {
fmt.Println(color.YellowString("Build succeeded for %s in %s", mode, timecost))
return
}
fmt.Println(color.GreenString("Build succeeded for %s in %s", mode, timecost))
}, },
} }

View file

@ -1,6 +1,10 @@
package sui package sui
var data string
var debug bool
func init() { func init() {
WatchCmd.PersistentFlags().StringVarP(&data, "data", "d", "::{}", L("Session Data")) WatchCmd.PersistentFlags().StringVarP(&data, "data", "d", "::{}", L("Session Data"))
BuildCmd.PersistentFlags().StringVarP(&data, "data", "d", "::{}", L("Session Data")) BuildCmd.PersistentFlags().StringVarP(&data, "data", "d", "::{}", L("Session Data"))
BuildCmd.PersistentFlags().BoolVarP(&debug, "debug", "D", false, L("Debug mode"))
} }

View file

@ -23,8 +23,6 @@ import (
"github.com/yaoapp/yao/sui/core" "github.com/yaoapp/yao/sui/core"
) )
var data string
var watched sync.Map var watched sync.Map
// WatchCmd command // WatchCmd command

8
go.mod
View file

@ -3,7 +3,7 @@ module github.com/yaoapp/yao
go 1.20 go 1.20
require ( require (
github.com/PuerkitoBio/goquery v1.8.1 github.com/PuerkitoBio/goquery v1.9.2
github.com/blang/semver v3.5.1+incompatible github.com/blang/semver v3.5.1+incompatible
github.com/caarlos0/env/v6 v6.10.1 github.com/caarlos0/env/v6 v6.10.1
github.com/dchest/captcha v1.0.0 github.com/dchest/captcha v1.0.0
@ -26,8 +26,8 @@ require (
github.com/yaoapp/gou v0.10.3 github.com/yaoapp/gou v0.10.3
github.com/yaoapp/kun v0.9.0 github.com/yaoapp/kun v0.9.0
github.com/yaoapp/xun v0.9.0 github.com/yaoapp/xun v0.9.0
golang.org/x/crypto v0.23.0 golang.org/x/crypto v0.25.0
golang.org/x/net v0.25.0 golang.org/x/net v0.27.0
golang.org/x/text v0.16.0 golang.org/x/text v0.16.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
@ -113,7 +113,7 @@ require (
golang.org/x/mod v0.17.0 // indirect golang.org/x/mod v0.17.0 // indirect
golang.org/x/oauth2 v0.14.0 // indirect golang.org/x/oauth2 v0.14.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.20.0 // indirect golang.org/x/sys v0.22.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/appengine v1.6.8 // indirect google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect

8
go.sum
View file

@ -1,5 +1,7 @@
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4=
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
@ -261,6 +263,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
@ -284,6 +288,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0= golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
@ -317,6 +323,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.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.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-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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=

46
service/gzip.go Normal file
View file

@ -0,0 +1,46 @@
package service
import (
"compress/gzip"
"net/http"
"strings"
)
// gzipHandler
func gzipHandler(h http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
h.ServeHTTP(w, r)
return
}
w.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(w)
defer gz.Close()
gzWriter := gzipResponseWriter{ResponseWriter: w, Writer: gz}
h.ServeHTTP(gzWriter, r)
}
}
type gzipResponseWriter struct {
http.ResponseWriter
*gzip.Writer
}
func (w gzipResponseWriter) WriteHeader(code int) {
w.ResponseWriter.Header().Del("Content-Length")
w.ResponseWriter.WriteHeader(code)
}
func (w gzipResponseWriter) Write(b []byte) (int, error) {
return w.Writer.Write(b)
}
func (w gzipResponseWriter) Flush() {
w.Writer.Flush()
}
func (w gzipResponseWriter) Header() http.Header {
return w.ResponseWriter.Header()
}

View file

@ -1,6 +1,9 @@
package service package service
import ( import (
"bytes"
"compress/gzip"
"net/http"
"path/filepath" "path/filepath"
"strings" "strings"
@ -42,15 +45,6 @@ func withStaticFileServer(c *gin.Context) {
return return
} }
// Yao Builder
// URL /yao/builder
if length >= 12 && c.Request.URL.Path[0:12] == "/yao/builder" {
c.Request.URL.Path = strings.TrimPrefix(c.Request.URL.Path, "/yao/builder")
BuilderFileServer.ServeHTTP(c.Writer, c.Request)
c.Abort()
return
}
// Rewrite // Rewrite
for _, rewrite := range rewriteRules { for _, rewrite := range rewriteRules {
// log.Debug("Rewrite: %s => %s", c.Request.URL.Path, rewrite.Replacement) // log.Debug("Rewrite: %s => %s", c.Request.URL.Path, rewrite.Replacement)
@ -67,8 +61,6 @@ func withStaticFileServer(c *gin.Context) {
// Sui file server // Sui file server
if strings.HasSuffix(c.Request.URL.Path, ".sui") { if strings.HasSuffix(c.Request.URL.Path, ".sui") {
log.Debug("Sui File: %s", c.Request.URL.Path)
// Default index.sui // Default index.sui
if filepath.Base(c.Request.URL.Path) == ".sui" { if filepath.Base(c.Request.URL.Path) == ".sui" {
c.Request.URL.Path = strings.TrimSuffix(c.Request.URL.Path, ".sui") + "index.sui" c.Request.URL.Path = strings.TrimSuffix(c.Request.URL.Path, ".sui") + "index.sui"
@ -96,9 +88,30 @@ func withStaticFileServer(c *gin.Context) {
return return
} }
// Gzip Compression
if strings.Contains(c.GetHeader("Accept-Encoding"), "gzip") {
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
if _, err := gz.Write([]byte(html)); err != nil {
log.Error("GZIP Compression Error: %s", err.Error())
c.AbortWithStatus(http.StatusInternalServerError)
return
}
if err := gz.Close(); err != nil {
log.Error("GZIP Close Error: %s", err.Error())
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.Header("Content-Type", "text/html; charset=utf-8")
c.Header("Content-Encoding", "gzip")
c.Data(http.StatusOK, "text/html", buf.Bytes())
c.Done()
}
c.Header("Content-Type", "text/html; charset=utf-8") c.Header("Content-Type", "text/html; charset=utf-8")
c.String(200, html) c.String(200, html)
c.Done() c.Next()
return return
} }

View file

@ -38,7 +38,7 @@ type rewriteRule struct {
func SetupStatic() error { func SetupStatic() error {
setupAdminRoot() setupAdminRoot()
setupRewrite() setupRewrite()
AppFileServer = http.FileServer(fs.Dir("public")) AppFileServer = gzipHandler(http.FileServer(fs.Dir("public")))
return nil return nil
} }

View file

@ -55,7 +55,7 @@ func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Docume
} }
// Scripts // Scripts
namespace := Namespace(page.Name, ctx.sequence) namespace := Namespace(page.Name, ctx.sequence, option.ScriptMinify)
scripts, err := page.BuildScripts(ctx, option, "__page", namespace) scripts, err := page.BuildScripts(ctx, option, "__page", namespace)
if err != nil { if err != nil {
return nil, ctx.warnings, err return nil, ctx.warnings, err
@ -99,8 +99,8 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op
return "", fmt.Errorf("The component tag must have an is attribute") return "", fmt.Errorf("The component tag must have an is attribute")
} }
namespace := Namespace(name, ctx.sequence) namespace := Namespace(name, ctx.sequence, option.ScriptMinify)
component := ComponentName(name) component := ComponentName(name, option.ScriptMinify)
attrs := []html.Attribute{ attrs := []html.Attribute{
{Key: "s:ns", Val: namespace}, {Key: "s:ns", Val: namespace},
{Key: "s:cn", Val: component}, {Key: "s:cn", Val: component},

View file

@ -64,6 +64,13 @@ var keepWords = map[string]bool{
"s:bind": true, "s:bind": true,
} }
var keepAttrs = map[string]bool{
"s:ns": true,
"s:cn": true,
"s:ready": true,
"s:click": true,
}
// Locales the locales // Locales the locales
var Locales = map[string]map[string]*Locale{} var Locales = map[string]map[string]*Locale{}
@ -229,8 +236,6 @@ func (parser *TemplateParser) Render(html string) (string, error) {
if parser.option != nil && (parser.option.Request || parser.option.Preview) { if parser.option != nil && (parser.option.Request || parser.option.Preview) {
// Remove the sui-hide attribute // Remove the sui-hide attribute
doc.Find("[sui-hide]").Remove() doc.Find("[sui-hide]").Remove()
// Remove All comments and used nodes
parser.tidy(doc.Selection) parser.tidy(doc.Selection)
} }
@ -271,6 +276,8 @@ func (parser *TemplateParser) parseNode(node *html.Node) {
func (parser *TemplateParser) parseElementNode(sel *goquery.Selection) { func (parser *TemplateParser) parseElementNode(sel *goquery.Selection) {
node := sel.Get(0)
if _, exist := sel.Attr("s:for"); exist { if _, exist := sel.Attr("s:for"); exist {
parser.forStatementNode(sel) parser.forStatementNode(sel)
} }
@ -279,7 +286,7 @@ func (parser *TemplateParser) parseElementNode(sel *goquery.Selection) {
parser.ifStatementNode(sel) parser.ifStatementNode(sel)
} }
if _, exist := sel.Attr("s:set"); exist || sel.Get(0).Data == "s:set" { if _, exist := sel.Attr("s:set"); exist || node.Data == "s:set" {
parser.setStatementNode(sel) parser.setStatementNode(sel)
} }
@ -288,6 +295,11 @@ func (parser *TemplateParser) parseElementNode(sel *goquery.Selection) {
parser.parseJitElementNode(sel) parser.parseJitElementNode(sel)
} }
// Slot Node
if node.Data == "slot" {
parser.slotNode(sel)
}
// Parse the attributes // Parse the attributes
parser.parseElementAttrs(sel) parser.parseElementAttrs(sel)
@ -361,6 +373,16 @@ func (parser *TemplateParser) transElementNode(sel *goquery.Selection) {
} }
} }
// Remove the slot tag and replace it with the children
func (parser *TemplateParser) slotNode(sel *goquery.Selection) {
children := sel.Children()
if children.Length() == 0 {
sel.Remove()
return
}
sel.ReplaceWithSelection(children)
}
func (parser *TemplateParser) setStatementNode(sel *goquery.Selection) { func (parser *TemplateParser) setStatementNode(sel *goquery.Selection) {
sel.SetAttr("parsed", "true") sel.SetAttr("parsed", "true")
@ -396,6 +418,12 @@ func (parser *TemplateParser) parseElementAttrs(sel *goquery.Selection) {
attrs := sel.Nodes[0].Attr attrs := sel.Nodes[0].Attr
for _, attr := range attrs { for _, attr := range attrs {
// Ignore the s: attributes
if strings.HasPrefix(attr.Key, "s:") {
continue
}
parser.sequence = parser.sequence + 1 parser.sequence = parser.sequence + 1
res, hasStmt := parser.data.Replace(attr.Val) res, hasStmt := parser.data.Replace(attr.Val)
if hasStmt { if hasStmt {
@ -672,30 +700,33 @@ func (parser *TemplateParser) show(sel *goquery.Selection) {
// sel.SetAttr("style", style) // sel.SetAttr("style", style)
} }
func (parser *TemplateParser) tidy(selection *goquery.Selection) { func (parser *TemplateParser) tidy(s *goquery.Selection) {
selection.Contents().Each(func(i int, s *goquery.Selection) {
if s.Nodes[0].Type == html.CommentNode { s.Contents().Each(func(i int, child *goquery.Selection) {
s.Remove()
node := child.Get(0)
if node.Type == html.CommentNode {
child = child.Remove()
return return
} }
// Remove the s:key-* attributes // Remove the parsed attribute
if s.Nodes[0].Type == html.ElementNode { attrs := []html.Attribute{}
for _, attr := range s.Nodes[0].Attr { for _, attr := range node.Attr {
if attr.Key == "parsed" || keepWords[attr.Key] || strings.HasPrefix(attr.Key, "s:key") || strings.HasPrefix(attr.Key, "s:bind") { if strings.HasPrefix(attr.Key, "s:") && !keepAttrs[attr.Key] {
s.RemoveAttr(attr.Key) continue
}
} }
if s.Nodes[0].Data == "s:set" { if attr.Key == "parsed" || attr.Key == "is" || strings.HasPrefix(attr.Key, "...") {
s.Remove() continue
return
} }
attrs = append(attrs, attr)
} }
parser.tidy(s) node.Attr = attrs
parser.tidy(child)
}) })
} }
func (parser *TemplateParser) key(prefix string, sel *goquery.Selection) string { func (parser *TemplateParser) key(prefix string, sel *goquery.Selection) string {

View file

@ -3,6 +3,7 @@ package core
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"hash/fnv"
"strings" "strings"
"github.com/PuerkitoBio/goquery" "github.com/PuerkitoBio/goquery"
@ -50,17 +51,29 @@ func NewDocumentStringWithWrapper(htmlContent string) (*goquery.Document, error)
} }
// Namespace convert the name to namespace // Namespace convert the name to namespace
func Namespace(name string, idx int) string { func Namespace(name string, idx int, hash ...bool) string {
name = strings.ReplaceAll(name, "/", "_") name = strings.ReplaceAll(name, "/", "_")
name = strings.ReplaceAll(name, "[", "_") name = strings.ReplaceAll(name, "[", "_")
name = strings.ReplaceAll(name, "]", "_") name = strings.ReplaceAll(name, "]", "_")
ns := fmt.Sprintf("__namespace_%s", name)
if len(hash) > 0 && hash[0] {
h := fnv.New64a()
h.Write([]byte(ns))
return fmt.Sprintf("ns_%x", h.Sum64())
}
return fmt.Sprintf("__page_%s_%d", name, idx) return fmt.Sprintf("__page_%s_%d", name, idx)
} }
// ComponentName convert the name to component name // ComponentName convert the name to component name
func ComponentName(name string) string { func ComponentName(name string, hash ...bool) string {
name = strings.ReplaceAll(name, "/", "_") name = strings.ReplaceAll(name, "/", "_")
name = strings.ReplaceAll(name, "[", "_") name = strings.ReplaceAll(name, "[", "_")
name = strings.ReplaceAll(name, "]", "_") name = strings.ReplaceAll(name, "]", "_")
return fmt.Sprintf("__component_%s", name) cn := fmt.Sprintf("__component_%s", name)
if len(hash) > 0 && hash[0] {
h := fnv.New64a()
h.Write([]byte(cn))
return fmt.Sprintf("cn_%x", h.Sum64())
}
return cn
} }