Refactor prop regex patterns and methods in SUI core

This commit is contained in:
Max 2024-07-19 16:08:00 +08:00
parent af3a8a4ac4
commit 69e0070b0a
3 changed files with 180 additions and 48 deletions

View file

@ -7,6 +7,7 @@ import (
"strings"
"github.com/PuerkitoBio/goquery"
"github.com/yaoapp/kun/log"
"golang.org/x/net/html"
)
@ -188,12 +189,15 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op
// Pass the component props
first := body.Children().First()
page.copyProps(ctx, sel, first, attrs...)
// page.copyProps(ctx, sel, first, attrs...)
page.parseProps(sel, first, attrs...)
page.copySlots(sel, first)
page.copyChildren(sel, first)
page.buildComponents(doc, ctx, &opt)
data := Data{"$props": page.Attrs}
data.ReplaceSelectionUse(slotRe, first)
page.replaceProps(first)
// data := Data{"$props": page.Attrs}
// data.ReplaceSelectionUse(slotRe, first)
// Add the translation marks
err = page.TranslateDocument(doc)
@ -262,54 +266,145 @@ func (page *Page) copyChildren(from *goquery.Selection, to *goquery.Selection) e
return nil
}
func (page *Page) copyProps(ctx *BuildContext, from *goquery.Selection, to *goquery.Selection, extra ...html.Attribute) error {
func (page *Page) parseProps(from *goquery.Selection, to *goquery.Selection, extra ...html.Attribute) {
attrs := from.Get(0).Attr
prefix := "s:prop"
if page.Attrs == nil {
page.Attrs = map[string]string{}
if page.props == nil {
page.props = map[string]PageProp{}
}
if attrs == nil {
attrs = []html.Attribute{}
}
for _, attr := range attrs {
if strings.HasPrefix(attr.Key, "s:") || attr.Key == "is" || attr.Key == "parsed" {
continue
}
if strings.HasPrefix(attr.Key, "...$props") {
data := Data{"$props": page.parent.Attrs}
val, err := data.Exec(fmt.Sprintf("{{ %s }}", attr.Key[3:]))
if err != nil {
ctx.warnings = append(ctx.warnings, err.Error())
setError(to, err)
continue
}
switch value := val.(type) {
case map[string]string:
for key, value := range value {
page.Attrs[key] = value
key = fmt.Sprintf("%s:%s", prefix, key)
to.SetAttr(key, value)
if attr.Key == "...$props" && page.parent != nil {
if page.parent.props != nil {
for key, prop := range page.parent.props {
page.props[key] = prop
}
}
continue
}
val := attr.Val
if strings.HasPrefix(attr.Key, `...\$props`) {
val = fmt.Sprintf("{{ $props.%s }}", attr.Key[9:])
if strings.HasPrefix(attr.Key, "...") && page.parent != nil {
val := attr.Key[3:]
key := fmt.Sprintf("s:prop:%s", attr.Key)
to.SetAttr(key, val)
continue
}
if strings.HasPrefix(attr.Key, "...") {
val = attr.Key[3:]
}
page.Attrs[attr.Key] = val
key := fmt.Sprintf("%s:%s", prefix, attr.Key)
to.SetAttr(key, val)
trans := from.AttrOr(fmt.Sprintf("s:trans-attr-%s", attr.Key), "")
exp := stmtRe.Match([]byte(attr.Val))
prop := PageProp{Key: attr.Key, Val: attr.Val, Trans: trans, Exp: exp}
page.props[attr.Key] = prop
}
if len(extra) > 0 {
if extra != nil && len(extra) > 0 {
for _, attr := range extra {
attrs = append(attrs, attr)
to.SetAttr(attr.Key, attr.Val)
}
}
}
func (page *Page) replaceProps(sel *goquery.Selection) error {
if page.props == nil || len(page.props) == 0 {
return nil
}
data := Data{}
for key, prop := range page.props {
data[key] = prop.Val
}
data["$props"] = data
return page.replacePropsNode(data, sel.Nodes[0])
}
func (page *Page) replacePropsText(text string, data Data) (string, []string) {
trans := []string{}
matched := PropFindAllStringSubmatch(text)
for _, match := range matched {
stmt := match[1]
val, err := data.ExecString(stmt)
if err != nil {
log.Error("[replaceProps] Replace %s: %s", stmt, err)
continue
}
text = strings.ReplaceAll(text, match[0], val)
vars := PropGetVarNames(stmt)
for _, v := range vars {
if v == "" {
continue
}
if prop, has := page.props[v]; has {
if prop.Trans == "" {
continue
}
trans = append(trans, prop.Trans)
}
}
}
return text, trans
}
func (page *Page) replacePropsNode(data Data, node *html.Node) error {
switch node.Type {
case html.TextNode:
text := node.Data
if strings.TrimSpace(text) == "" {
break
}
text, trans := page.replacePropsText(text, data)
if len(trans) > 0 && node.Parent != nil {
node.Parent.Attr = append(node.Parent.Attr, html.Attribute{
Key: "s:trans-text",
Val: strings.Join(trans, ","),
})
}
node.Data = text
break
case html.ElementNode:
// Attrs
attrs := []html.Attribute{}
for i, attr := range node.Attr {
if (strings.HasPrefix(attr.Key, "s:") || attr.Key == "is") && !allowUsePropAttrs[attr.Key] {
continue
}
val, trans := page.replacePropsText(attr.Val, data)
node.Attr[i] = html.Attribute{Key: attr.Key, Val: val}
if len(trans) > 0 {
key := fmt.Sprintf("s:trans-attr-%s", attr.Key)
attrs = append(attrs, html.Attribute{Key: key, Val: strings.Join(trans, ",")})
}
}
for _, attr := range attrs {
node.Attr = append(node.Attr, attr)
}
// Children
for c := node.FirstChild; c != nil; c = c.NextSibling {
err := page.replacePropsNode(data, c)
if err != nil {
return err
}
}
break
}
return nil
}

View file

@ -16,7 +16,9 @@ import (
// If set the map value, should keep the space at the end of the statement
var stmtRe = regexp.MustCompile(`\{\{([\s\S]*?)\}\}`)
var propRe = regexp.MustCompile(`\[\{([\s\S]*?)\}\]`)
var propRe = regexp.MustCompile(`\[\{([\s\S]*?)\}\]`) // [{ xxx }] will be deprecated
var propNewRe = regexp.MustCompile(`\{%([\s\S]*)?%\}`) // {% xxx %}
var propVarNameRe = regexp.MustCompile(`(?:\$props\.)?(?:$begin:math:display$'([^']+)'$end:math:display$|(\w+))`)
// Data data for the template
type Data map[string]interface{}
@ -184,3 +186,29 @@ func _process(args ...any) (interface{}, error) {
return res, nil
}
// PropFindAllStringSubmatch find all string submatch
func PropFindAllStringSubmatch(value string) [][]string {
matched := propNewRe.FindAllStringSubmatch(value, -1)
oldVersion := propRe.FindAllStringSubmatch(value, -1) // will be deprecated
if len(oldVersion) > 0 {
matched = append(matched, oldVersion...)
}
return matched
}
// PropGetVarNames get the variable names
func PropGetVarNames(value string) []string {
matched := propVarNameRe.FindAllStringSubmatch(value, -1)
varNames := []string{}
for _, m := range matched {
m1 := strings.TrimSpace(m[1])
m2 := strings.TrimSpace(m[2])
if m1 != "" {
varNames = append(varNames, m1)
} else {
varNames = append(varNames, m2)
}
}
return varNames
}

View file

@ -29,22 +29,31 @@ type Setting struct {
// Page is the struct for the page
type Page struct {
Route string `json:"route"`
Name string `json:"name,omitempty"`
CacheStore string `json:"-"`
TemplateID string `json:"-"`
SuiID string `json:"-"`
Config *PageConfig `json:"-"`
Path string `json:"-"`
Root string `json:"-"`
Codes SourceCodes `json:"-"`
Document []byte `json:"-"`
GlobalData []byte `json:"-"`
Attrs map[string]string `json:"-"`
Attributes []html.Attribute `json:"-"`
namespace string `json:"-"`
transCtx *TranslateContext `json:"-"`
parent *Page `json:"-"`
Route string `json:"route"`
Name string `json:"name,omitempty"`
CacheStore string `json:"-"`
TemplateID string `json:"-"`
SuiID string `json:"-"`
Config *PageConfig `json:"-"`
Path string `json:"-"`
Root string `json:"-"`
Codes SourceCodes `json:"-"`
Document []byte `json:"-"`
GlobalData []byte `json:"-"`
Attrs map[string]string `json:"-"`
Attributes []html.Attribute `json:"-"`
namespace string `json:"-"`
transCtx *TranslateContext `json:"-"`
parent *Page `json:"-"`
props map[string]PageProp `json:"-"`
}
// PageProp is the struct for the page prop
type PageProp struct {
Key string `json:"key"`
Val string `json:"val"`
Exp bool `json:"exp"`
Trans string `json:"trans"`
}
// BuildContext is the struct for the build context