diff --git a/sui/core/compile.go b/sui/core/compile.go index 84b94981..a83bbf9d 100644 --- a/sui/core/compile.go +++ b/sui/core/compile.go @@ -266,6 +266,9 @@ func (script ScriptNode) ComponentHTML(ns string) string { } source := fmt.Sprintf(`function %s(){%s};`, script.Component, script.Source) + if script.Component == "" { + return "" + } return "" } diff --git a/sui/core/event.go b/sui/core/event.go index ab4b7345..b231de48 100644 --- a/sui/core/event.go +++ b/sui/core/event.go @@ -9,22 +9,38 @@ import ( "golang.org/x/net/html" ) +var eventMatcher = NewAttrPrefixMatcher(`s:on-`) + // BindEvent is a method that binds events to the page. -func (page *Page) BindEvent(ctx *BuildContext, sel *goquery.Selection, component string, ispage bool) { - matcher := NewAttrPrefixMatcher(`s:on-`) - sel.FindMatcher(matcher).Each(func(i int, s *goquery.Selection) { +func (page *Page) BindEvent(ctx *BuildContext, sel *goquery.Selection, cn string, ispage bool) { + + sel.FindMatcher(eventMatcher).Each(func(i int, s *goquery.Selection) { if comp, has := s.Attr("is"); has && ctx.isJitComponent(comp) { return } - script := GetEventScript(&ctx.sequence, s, page.namespace, component, ispage) + script := GetEventScript(ctx.sequence, s, page.namespace, cn, "event", ispage) if script != nil { ctx.scripts = append(ctx.scripts, *script) + ctx.sequence++ + } + }) +} + +// BindEvent is a method that binds events to the component in just-in-time mode. +func (parser *TemplateParser) BindEvent(sel *goquery.Selection, ns string, cn string) { + sel.FindMatcher(eventMatcher).Each(func(i int, s *goquery.Selection) { + script := GetEventScript(parser.sequence, s, ns, cn, "event-jit", false) + if script != nil { + script.Component = "" + script.Parent = "body" + parser.scripts = append(parser.scripts, *script) + parser.sequence++ } }) } // GetEventScript the event script -func GetEventScript(sequence *int, sel *goquery.Selection, ns string, cn string, ispage bool) *ScriptNode { +func GetEventScript(sequence int, sel *goquery.Selection, ns string, cn string, prefix string, ispage bool) *ScriptNode { if len(sel.Nodes) == 0 { return nil @@ -34,9 +50,7 @@ func GetEventScript(sequence *int, sel *goquery.Selection, ns string, cn string, events := map[string]string{} dataUnique := map[string]string{} jsonUnique := map[string]string{} - id := fmt.Sprintf("event-%d", *sequence) - *sequence = *sequence + 1 - + id := fmt.Sprintf("%s-%d", prefix, sequence) for _, attr := range sel.Nodes[0].Attr { if strings.HasPrefix(attr.Key, "s:on-") { diff --git a/sui/core/jit.go b/sui/core/jit.go index 9803d4ae..64737c86 100644 --- a/sui/core/jit.go +++ b/sui/core/jit.go @@ -6,7 +6,6 @@ import ( "path/filepath" "regexp" "strings" - "time" "github.com/PuerkitoBio/goquery" jsoniter "github.com/json-iterator/go" @@ -69,15 +68,17 @@ func (parser *TemplateParser) parseComponent(sel *goquery.Selection) { // RenderComponent render the component func (parser *TemplateParser) RenderComponent(comp *JitComponent, props map[string]interface{}, slots *goquery.Selection, children *goquery.Selection) (string, string, error) { html := comp.html - randvar := fmt.Sprintf("__%s_$props", time.Now().Format("20060102150405")) - html = replaceRandVar(html, randvar) - data := Data{} - data[randvar] = props - if parser.data != nil { - for key, val := range parser.data { - data[key] = val - } - } + + html = replaceRandVar(html, Data(props)) + option := *parser.option + option.Route = comp.route + compParser := NewTemplateParser(parser.data, &option) + compParser.sequence = parser.sequence + 1 + locale := compParser.Locale() + + // Parse the node + ns := Namespace(comp.route, compParser.sequence, comp.buildOption.ScriptMinify) + cn := ComponentName(comp.route, comp.buildOption.ScriptMinify) sel, err := NewDocumentString(`` + html + ``) if err != nil { @@ -107,24 +108,26 @@ func (parser *TemplateParser) RenderComponent(comp *JitComponent, props map[stri children.Find("slot").Remove() root.Find("children").ReplaceWithSelection(children) - option := *parser.option - option.Route = comp.route - compParser := NewTemplateParser(data, &option) - locale := compParser.Locale() + // Replace the props if locale != nil { - locale.replaceVars(randvar) + locale.replaceVars(Data(props)) } - compParser.locale = locale - // Parse the node - ns := Namespace(comp.route, compParser.sequence, comp.buildOption.ScriptMinify) - cn := ComponentName(comp.route, comp.buildOption.ScriptMinify) + compParser.locale = locale + compParser.BindEvent(root, ns, cn) compParser.parseNode(root.Get(0)) for sel, nodes := range compParser.replace { sel.ReplaceWithNodes(nodes...) delete(parser.replace, sel) } + if compParser.scripts != nil { + for _, script := range compParser.scripts { + script.Namespace = ns + parser.scripts = append(parser.scripts, script) + } + } + if comp.scripts != nil { for _, script := range comp.scripts { script.Namespace = ns @@ -145,6 +148,7 @@ func (parser *TemplateParser) RenderComponent(comp *JitComponent, props map[stri first.SetAttr("s:ns", ns) first.SetAttr("s:cn", cn) first.SetAttr("s:ready", cn+"()") + html, err = root.Html() if err != nil { return "", "", err @@ -152,15 +156,28 @@ func (parser *TemplateParser) RenderComponent(comp *JitComponent, props map[stri return html, ns, nil } -func (parser *TemplateParser) addScripts(sel *goquery.Selection, scripts []ScriptNode) { +func (parser *TemplateParser) filterScripts(parent string, scripts []ScriptNode) []ScriptNode { if scripts == nil { - return + return []ScriptNode{} } + filtered := []ScriptNode{} for _, script := range scripts { - query := fmt.Sprintf(`script[s\:cn="%s"]`, script.Component) - if sel.Find(query).Length() > 0 { + if script.Parent != parent { continue } + filtered = append(filtered, script) + } + return filtered +} + +func (parser *TemplateParser) addScripts(sel *goquery.Selection, scripts []ScriptNode) { + for _, script := range scripts { + if script.Component != "" { + query := fmt.Sprintf(`script[s\:cn="%s"]`, script.Component) + if sel.Find(query).Length() > 0 { + continue + } + } sel.AppendHtml(script.ComponentHTML(script.Namespace)) } } @@ -227,6 +244,13 @@ func (parser *TemplateParser) componentProps(sel *goquery.Selection) (map[string } for _, attr := range sel.Nodes[0].Attr { + + // s:on , s:data , s:json + if strings.HasPrefix(attr.Key, "s:on") || strings.HasPrefix(attr.Key, "s:data") || strings.HasPrefix(attr.Key, "s:json") { + props[attr.Key] = attr.Val + continue + } + if strings.HasPrefix(attr.Key, "s:") || attr.Key == "is" { continue } @@ -269,22 +293,13 @@ func (parser *TemplateParser) parseComponentProps(props map[string]string) (map[ } if _, ok := values.(map[string]interface{}); ok { - for k, v := range values.(map[string]interface{}) { - result[k] = v + for k := range values.(map[string]interface{}) { + result[k] = k } } continue } - if strings.HasPrefix(val, "{{") && strings.HasSuffix(val, "}}") { - value, err := parser.data.Exec(val) - if err != nil { - return map[string]interface{}{}, err - } - result[key] = value - continue - } - result[key] = val } return result, nil @@ -304,16 +319,16 @@ func (parser *TemplateParser) componentFile(sel *goquery.Selection, props map[st return file, route, nil } -func (locale *Locale) replaceVars(randvar string) { +func (locale *Locale) replaceVars(data Data) { if locale.Keys != nil { for key, val := range locale.Keys { - locale.Keys[key] = replaceRandVar(val, randvar) + locale.Keys[key] = replaceRandVar(val, data) } } if locale.Messages != nil { for key, val := range locale.Messages { - locale.Messages[key] = replaceRandVar(val, randvar) + locale.Messages[key] = replaceRandVar(val, data) } } } @@ -415,11 +430,18 @@ func readComponent(route string, file string) (*JitComponent, error) { }, nil } -func replaceRandVar(html string, randvar string) string { - return slotRe.ReplaceAllStringFunc(html, func(exp string) string { - exp = strings.ReplaceAll(exp, "[{", "{{") - exp = strings.ReplaceAll(exp, "}]", "}}") - exp = strings.ReplaceAll(exp, "$props", randvar) - return exp +func replaceRandVar(value string, data Data) string { + + value = propNewRe.ReplaceAllStringFunc(value, func(exp string) string { + exp = strings.TrimPrefix(exp, "{%") + exp = strings.TrimSuffix(exp, "%}") + res, _ := data.ExecString(fmt.Sprintf("{{ %s }}", exp)) + return res + }) + + data = Data{"$props": data} + return slotRe.ReplaceAllStringFunc(value, func(exp string) string { + res, _ := data.ExecString(exp) + return res }) } diff --git a/sui/core/parser.go b/sui/core/parser.go index 175f987e..a3db3bed 100644 --- a/sui/core/parser.go +++ b/sui/core/parser.go @@ -134,7 +134,7 @@ func (parser *TemplateParser) Render(html string) (string, error) { } head.AppendHtml(headInjectionScript(data)) - parser.addScripts(head, parser.scripts) + parser.addScripts(head, parser.filterScripts("head", parser.scripts)) parser.addStyles(head, parser.styles) } @@ -146,6 +146,7 @@ func (parser *TemplateParser) Render(html string) (string, error) { data, _ = jsoniter.MarshalToString(map[string]string{"error": err.Error()}) } body.AppendHtml(bodyInjectionScript(data, parser.debug())) + parser.addScripts(body, parser.filterScripts("body", parser.scripts)) } // Fmt