Merge pull request #713 from trheyi/main
Optimize SUI frontend and backend script data sharing
This commit is contained in:
commit
dbf2be8ce8
8 changed files with 261 additions and 334 deletions
|
|
@ -133,14 +133,14 @@ func (r *Request) Render() (string, int, error) {
|
||||||
if c.Data != "" {
|
if c.Data != "" {
|
||||||
err = r.Request.ExecStringMerge(data, c.Data)
|
err = r.Request.ExecStringMerge(data, c.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 500, fmt.Errorf("data error, please re-complie the page %s", err.Error())
|
return "", 500, fmt.Errorf("data error, please re-complie the page. %s", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Global != "" {
|
if c.Global != "" {
|
||||||
global, err := r.Request.ExecString(c.Global)
|
global, err := r.Request.ExecString(c.Global)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 500, fmt.Errorf("global data error, please re-complie the page %s", err.Error())
|
return "", 500, fmt.Errorf("global data error, please re-complie the page. %s", err.Error())
|
||||||
}
|
}
|
||||||
data["$global"] = global
|
data["$global"] = global
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,10 @@ func (page *Page) parseProps(from *goquery.Selection, to *goquery.Selection, ext
|
||||||
exp := dataTokens.MatchString(attr.Val)
|
exp := dataTokens.MatchString(attr.Val)
|
||||||
prop := PageProp{Key: attr.Key, Val: attr.Val, Trans: trans, Exp: exp}
|
prop := PageProp{Key: attr.Key, Val: attr.Val, Trans: trans, Exp: exp}
|
||||||
page.props[attr.Key] = prop
|
page.props[attr.Key] = prop
|
||||||
to.SetAttr(attr.Key, attr.Val)
|
|
||||||
|
// Set the prop
|
||||||
|
key := fmt.Sprintf("prop:%s", attr.Key)
|
||||||
|
to.SetAttr(key, attr.Val)
|
||||||
}
|
}
|
||||||
|
|
||||||
if extra != nil && len(extra) > 0 {
|
if extra != nil && len(extra) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -142,14 +142,10 @@ func (page *Page) CompileAsComponent(ctx *BuildContext, option *BuildOption) (st
|
||||||
return "", warnings, err
|
return "", warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Page Components
|
rawComponents := "{}"
|
||||||
components := []string{}
|
|
||||||
if ctx != nil && ctx.components != nil && len(ctx.components) > 0 {
|
if ctx != nil && ctx.components != nil && len(ctx.components) > 0 {
|
||||||
for route := range ctx.components {
|
rawComponents, _ = jsoniter.MarshalToString(ctx.components)
|
||||||
components = append(components, route)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
rawComponents, _ := jsoniter.MarshalToString(components)
|
|
||||||
|
|
||||||
if body.Children().Length() == 0 {
|
if body.Children().Length() == 0 {
|
||||||
return "", warnings, fmt.Errorf("page %s as component should have one root element", page.Route)
|
return "", warnings, fmt.Errorf("page %s as component should have one root element", page.Route)
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,46 @@ const libsuisource = `
|
||||||
|
|
||||||
function __sui_props(elm) {
|
function __sui_props(elm) {
|
||||||
this.Get = function (key) {
|
this.Get = function (key) {
|
||||||
return elm && elm.getAttribute(key);
|
if (!elm || typeof elm.getAttribute !== "function") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const k = "prop:" + key;
|
||||||
|
const v = elm.getAttribute(k);
|
||||||
|
const json = elm.getAttribute("json-attr-prop:" + key) === "true";
|
||||||
|
if (json) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(v);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.List = function () {
|
||||||
|
const props = {};
|
||||||
|
if (!elm || typeof elm.getAttribute !== "function") {
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attrs = elm.attributes;
|
||||||
|
for (let i = 0; i < attrs.length; i++) {
|
||||||
|
const attr = attrs[i];
|
||||||
|
if (attr.name.startsWith("prop:")) {
|
||||||
|
const k = attr.name.replace("prop:", "");
|
||||||
|
const json = elm.getAttribute("json-attr-prop:" + k) === "true";
|
||||||
|
if (json) {
|
||||||
|
try {
|
||||||
|
props[k] = JSON.parse(attr.value);
|
||||||
|
} catch (e) {
|
||||||
|
props[k] = null;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
props[k] = attr.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,6 +162,10 @@ const libsuisource = `
|
||||||
this.SetJSON = function (key, value) {
|
this.SetJSON = function (key, value) {
|
||||||
elm.setAttribute("json:" + key, JSON.stringify(value));
|
elm.setAttribute("json:" + key, JSON.stringify(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.GetData = function () {
|
||||||
|
return this.GetJSON("__component_data") || {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -184,6 +227,7 @@ const compEventScriptTmpl = `
|
||||||
const componentInitScriptTmpl = `
|
const componentInitScriptTmpl = `
|
||||||
this.root = %s;
|
this.root = %s;
|
||||||
this.store = new __sui_store(this.root);
|
this.store = new __sui_store(this.root);
|
||||||
|
this.props = new __sui_props(this.root);
|
||||||
`
|
`
|
||||||
|
|
||||||
// Inject code
|
// Inject code
|
||||||
|
|
|
||||||
501
sui/core/jit.go
501
sui/core/jit.go
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/yaoapp/gou/application"
|
"github.com/yaoapp/gou/application"
|
||||||
)
|
)
|
||||||
|
|
@ -19,6 +20,7 @@ type JitComponent struct {
|
||||||
html string
|
html string
|
||||||
scripts []ScriptNode
|
scripts []ScriptNode
|
||||||
styles []StyleNode
|
styles []StyleNode
|
||||||
|
imports map[string]string
|
||||||
buildOption *BuildOption
|
buildOption *BuildOption
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,6 +41,7 @@ var chComp = make(chan *componentData, 1)
|
||||||
var reScripts = regexp.MustCompile(`<script[^>]*name="scripts"[^>]*>(.*?)</script>`)
|
var reScripts = regexp.MustCompile(`<script[^>]*name="scripts"[^>]*>(.*?)</script>`)
|
||||||
var reStyles = regexp.MustCompile(`<script[^>]*name="styles"[^>]*>(.*?)</script>`)
|
var reStyles = regexp.MustCompile(`<script[^>]*name="styles"[^>]*>(.*?)</script>`)
|
||||||
var reOption = regexp.MustCompile(`<script[^>]*name="option"[^>]*>(.*?)</script>`)
|
var reOption = regexp.MustCompile(`<script[^>]*name="option"[^>]*>(.*?)</script>`)
|
||||||
|
var reImports = regexp.MustCompile(`<script[^>]*name="imports"[^>]*>(.*?)</script>`)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
go componentWriter()
|
go componentWriter()
|
||||||
|
|
@ -47,119 +50,222 @@ func init() {
|
||||||
// parseComponent parse the component
|
// parseComponent parse the component
|
||||||
func (parser *TemplateParser) parseJitComponent(sel *goquery.Selection) {
|
func (parser *TemplateParser) parseJitComponent(sel *goquery.Selection) {
|
||||||
parser.parsed(sel)
|
parser.parsed(sel)
|
||||||
comp, props, slots, children, err := parser.getComponent(sel)
|
comp, err := parser.getJitComponent(sel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
parser.errors = append(parser.errors, err)
|
parser.errors = append(parser.errors, err)
|
||||||
setError(sel, err)
|
setError(sel, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
html, _, err := parser.RenderComponent(comp, props, slots, children)
|
comsel, err := parser.newJitComponentSel(sel, comp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
parser.errors = append(parser.errors, err)
|
parser.errors = append(parser.errors, err)
|
||||||
setError(sel, err)
|
setError(sel, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
parser.parseElementComponent(comsel)
|
||||||
// fmt.Println(sel.Nodes[0].Attr)
|
sel.ReplaceWithSelection(comsel)
|
||||||
sel.SetHtml(html)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderComponent render the component
|
func (parser *TemplateParser) newJitComponentSel(sel *goquery.Selection, comp *JitComponent) (*goquery.Selection, error) {
|
||||||
func (parser *TemplateParser) RenderComponent(comp *JitComponent, props map[string]interface{}, slots *goquery.Selection, children *goquery.Selection) (string, string, error) {
|
|
||||||
html := comp.html
|
|
||||||
|
|
||||||
fmt.Println("---", comp.route, "----------------")
|
ns := Namespace(comp.route, parser.sequence+1, comp.buildOption.ScriptMinify)
|
||||||
fmt.Println(html)
|
|
||||||
fmt.Println("-------------------------------")
|
|
||||||
fmt.Println()
|
|
||||||
html = replaceRandVar(html, Data(props))
|
|
||||||
fmt.Println("---- after replaceRandVar ----------------")
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Println(html)
|
|
||||||
fmt.Println("-------------------------------")
|
|
||||||
fmt.Println()
|
|
||||||
|
|
||||||
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)
|
cn := ComponentName(comp.route, comp.buildOption.ScriptMinify)
|
||||||
|
props := map[string]string{
|
||||||
sel, err := NewDocumentString(`<body>` + html + `</body>`)
|
"s:ns": ns,
|
||||||
if err != nil {
|
"s:cn": cn,
|
||||||
return "", "", err
|
"s:ready": cn + "()",
|
||||||
|
}
|
||||||
|
data := Data{}
|
||||||
|
for _, attr := range sel.Nodes[0].Attr {
|
||||||
|
if attr.Key == "is" || attr.Key == "s:jit" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
props[attr.Key] = attr.Val
|
||||||
}
|
}
|
||||||
|
|
||||||
root := sel.Find("body")
|
// JSON props
|
||||||
|
for key, val := range props {
|
||||||
|
var v interface{} = val
|
||||||
|
if props[fmt.Sprintf("json-attr-%s", key)] == "true" {
|
||||||
|
fmt.Println("json-attr-", key)
|
||||||
|
err := jsoniter.UnmarshalFromString(val, &v)
|
||||||
|
if err != nil {
|
||||||
|
v = fmt.Sprintf(`"%s"`, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data[key] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
doc, err := NewDocumentString(comp.html)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Component %s failed to load, please recompile the component. %s", comp.route, err.Error())
|
||||||
|
}
|
||||||
|
compSel := doc.Find("body").Children().First()
|
||||||
|
data.replaceNodeUse(propTokens, compSel.Nodes[0])
|
||||||
|
for key, val := range props {
|
||||||
|
if strings.HasPrefix(key, "s:") || strings.HasPrefix(key, "json-attr-") || key == "parsed" {
|
||||||
|
compSel.SetAttr(key, val)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
compSel.SetAttr(fmt.Sprintf("prop:%s", key), val)
|
||||||
|
}
|
||||||
|
|
||||||
// Replace the slots
|
// Replace the slots
|
||||||
slots.Each(func(i int, s *goquery.Selection) {
|
slots := sel.Find("slot")
|
||||||
name := s.AttrOr("name", "")
|
if slots.Length() > 0 {
|
||||||
if name == "" {
|
for i := 0; i < slots.Length(); i++ {
|
||||||
return
|
name := slots.Eq(i).AttrOr("name", "")
|
||||||
}
|
if name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Find the slot
|
slots.Eq(i).Remove()
|
||||||
slotSel := root.Find(name)
|
slotSel := compSel.Find(name)
|
||||||
if slotSel.Length() == 0 {
|
if slotSel.Length() == 0 {
|
||||||
return
|
continue
|
||||||
|
}
|
||||||
|
slotSel.ReplaceWithSelection(slots.Eq(i).Contents())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Replace the slot
|
|
||||||
slotSel.ReplaceWithSelection(s.Contents())
|
|
||||||
})
|
|
||||||
|
|
||||||
// Replace the children
|
// Replace the children
|
||||||
children.Find("slot").Remove()
|
children := sel.Contents()
|
||||||
root.Find("children").ReplaceWithSelection(children)
|
compSel.Find("children").ReplaceWithSelection(children)
|
||||||
|
return compSel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (parser *TemplateParser) getJitComponent(sel *goquery.Selection) (*JitComponent, error) {
|
||||||
|
is := sel.AttrOr("is", "")
|
||||||
|
if is == "" {
|
||||||
|
return nil, fmt.Errorf("Component route is required")
|
||||||
|
|
||||||
// Replace the props
|
|
||||||
if locale != nil {
|
|
||||||
locale.replaceVars(Data(props))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compParser.locale = locale
|
if parser.option == nil {
|
||||||
compParser.BindEvent(root, ns, cn)
|
parser.option = &ParserOption{Debug: true, DisableCache: false}
|
||||||
compParser.RenderSelection(root)
|
|
||||||
|
|
||||||
if compParser.scripts != nil {
|
|
||||||
for _, script := range compParser.scripts {
|
|
||||||
script.Namespace = ns
|
|
||||||
parser.scripts = append(parser.scripts, script)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if comp.scripts != nil {
|
// Load the component
|
||||||
for _, script := range comp.scripts {
|
if comp, has := Components[is]; has && parser.option.Debug == false && parser.option.DisableCache == false {
|
||||||
script.Namespace = ns
|
return comp, nil
|
||||||
parser.scripts = append(parser.scripts, script)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if comp.styles != nil {
|
file := filepath.Join(string(os.PathSeparator), "public", parser.option.Root, is+".jit")
|
||||||
for _, style := range comp.styles {
|
if exist, _ := application.App.Exists(file); !exist {
|
||||||
style.Namespace = ns
|
return nil, fmt.Errorf("Component %s file not found, please recompile the component", is)
|
||||||
parser.styles = append(parser.styles, style)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update sequence
|
source, err := application.App.Read(file)
|
||||||
parser.sequence = compParser.sequence + parser.sequence
|
|
||||||
first := root.Children().First()
|
|
||||||
first.SetAttr("s:ns", ns)
|
|
||||||
first.SetAttr("s:cn", cn)
|
|
||||||
first.SetAttr("s:ready", cn+"()")
|
|
||||||
|
|
||||||
html, err = root.Html()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, fmt.Errorf("Component %s failed to load, please recompile the component", is)
|
||||||
}
|
}
|
||||||
return html, ns, nil
|
|
||||||
|
// Get the scripts
|
||||||
|
var scriptnodes []ScriptNode = []ScriptNode{}
|
||||||
|
var stylenodes []StyleNode = []StyleNode{}
|
||||||
|
var imports map[string]string = map[string]string{}
|
||||||
|
var buildOption *BuildOption = &BuildOption{}
|
||||||
|
|
||||||
|
source, scriptnodes, err = parser.getScriptNodes(source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Component %s failed to load, please recompile the component. %s", is, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
source, stylenodes, err = parser.getStyleNodes(source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Component %s failed to load, please recompile the component. %s", is, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
source, imports, err = parser.getImports(source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Component %s failed to load, please recompile the component. %s", is, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
source, buildOption, err = parser.getBuildOption(source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Component %s failed to load, please recompile the component. %s", is, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
comp := &JitComponent{
|
||||||
|
file: file,
|
||||||
|
route: is,
|
||||||
|
html: string(source),
|
||||||
|
scripts: scriptnodes,
|
||||||
|
styles: stylenodes,
|
||||||
|
imports: imports,
|
||||||
|
buildOption: buildOption,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the component to the cache
|
||||||
|
chComp <- &componentData{is, comp, saveComponent}
|
||||||
|
return comp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (parser *TemplateParser) getImports(source []byte) ([]byte, map[string]string, error) {
|
||||||
|
imports := map[string]string{}
|
||||||
|
source = reImports.ReplaceAllFunc(source, func(raw []byte) []byte {
|
||||||
|
raw = reImports.ReplaceAll(raw, []byte("$1"))
|
||||||
|
err := jsoniter.Unmarshal(raw, &imports)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []byte{}
|
||||||
|
})
|
||||||
|
return source, imports, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (parser *TemplateParser) getBuildOption(source []byte) ([]byte, *BuildOption, error) {
|
||||||
|
var buildOption BuildOption
|
||||||
|
rawOption := []byte{}
|
||||||
|
source = reOption.ReplaceAllFunc(source, func(raw []byte) []byte {
|
||||||
|
rawOption = reOption.ReplaceAll(raw, []byte("$1"))
|
||||||
|
return []byte{}
|
||||||
|
})
|
||||||
|
|
||||||
|
if rawOption != nil {
|
||||||
|
err := jsoniter.Unmarshal(rawOption, &buildOption)
|
||||||
|
if err != nil {
|
||||||
|
return source, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return source, &buildOption, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (parser *TemplateParser) getStyleNodes(source []byte) ([]byte, []StyleNode, error) {
|
||||||
|
var errs error
|
||||||
|
nodes := []StyleNode{}
|
||||||
|
source = reStyles.ReplaceAllFunc(source, func(raw []byte) []byte {
|
||||||
|
raw = reStyles.ReplaceAll(raw, []byte("$1"))
|
||||||
|
var stylenodes []StyleNode
|
||||||
|
err := jsoniter.Unmarshal(raw, &stylenodes)
|
||||||
|
if err != nil {
|
||||||
|
errs = multierror.Append(errs, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
nodes = append(nodes, stylenodes...)
|
||||||
|
return []byte{}
|
||||||
|
})
|
||||||
|
return source, nodes, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (parser *TemplateParser) getScriptNodes(source []byte) ([]byte, []ScriptNode, error) {
|
||||||
|
var errs error
|
||||||
|
nodes := []ScriptNode{}
|
||||||
|
|
||||||
|
// Get the scripts
|
||||||
|
source = reScripts.ReplaceAllFunc(source, func(raw []byte) []byte {
|
||||||
|
raw = reScripts.ReplaceAll(raw, []byte("$1"))
|
||||||
|
var scripts []ScriptNode
|
||||||
|
err := jsoniter.Unmarshal(raw, &scripts)
|
||||||
|
if err != nil {
|
||||||
|
errs = multierror.Append(errs, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
nodes = append(nodes, scripts...)
|
||||||
|
return []byte{}
|
||||||
|
})
|
||||||
|
return source, nodes, errs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *TemplateParser) filterScripts(parent string, scripts []ScriptNode) []ScriptNode {
|
func (parser *TemplateParser) filterScripts(parent string, scripts []ScriptNode) []ScriptNode {
|
||||||
|
|
@ -201,29 +307,6 @@ func (parser *TemplateParser) addStyles(sel *goquery.Selection, styles []StyleNo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *TemplateParser) getComponent(sel *goquery.Selection) (*JitComponent, map[string]interface{}, *goquery.Selection, *goquery.Selection, error) {
|
|
||||||
|
|
||||||
slots := sel.Find("slot")
|
|
||||||
children := sel.Contents()
|
|
||||||
|
|
||||||
props, err := parser.componentProps(sel)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, slots, children, err
|
|
||||||
}
|
|
||||||
|
|
||||||
file, route, err := parser.componentFile(sel, props)
|
|
||||||
if err != nil {
|
|
||||||
return nil, props, slots, children, err
|
|
||||||
}
|
|
||||||
|
|
||||||
comp, err := getComponent(route, file, parser.disableCache())
|
|
||||||
if err != nil {
|
|
||||||
return nil, props, slots, children, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return comp, props, slots, children, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// isJitComponent check if the selection is a component
|
// isJitComponent check if the selection is a component
|
||||||
func (parser *TemplateParser) isJitComponent(sel *goquery.Selection) bool {
|
func (parser *TemplateParser) isJitComponent(sel *goquery.Selection) bool {
|
||||||
_, exist := sel.Attr("s:jit")
|
_, exist := sel.Attr("s:jit")
|
||||||
|
|
@ -231,113 +314,6 @@ func (parser *TemplateParser) isJitComponent(sel *goquery.Selection) bool {
|
||||||
return exist && is != ""
|
return exist && is != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *TemplateParser) componentProps(sel *goquery.Selection) (map[string]interface{}, error) {
|
|
||||||
|
|
||||||
parentProps := map[string]string{}
|
|
||||||
props := map[string]string{}
|
|
||||||
parent := sel.AttrOr("s:parent", "")
|
|
||||||
parentSel := sel.Parents().Find(fmt.Sprintf(`[s\:ns="%s"]`, parent))
|
|
||||||
|
|
||||||
if parentSel != nil && parentSel.Length() > 0 {
|
|
||||||
for _, attr := range parentSel.Nodes[0].Attr {
|
|
||||||
|
|
||||||
if !strings.HasPrefix(attr.Key, "s:prop") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
key := strings.TrimPrefix(attr.Key, "s:prop:")
|
|
||||||
parentProps[key] = attr.Val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(attr.Key, "...$props") {
|
|
||||||
data := Data{"$props": parentProps}
|
|
||||||
values, _, err := data.Exec(fmt.Sprintf("{{ %s }}", strings.TrimPrefix(attr.Key, "...")))
|
|
||||||
if err != nil {
|
|
||||||
return map[string]interface{}{}, err
|
|
||||||
}
|
|
||||||
if values == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := values.(map[string]string); ok {
|
|
||||||
for key, val := range values.(map[string]string) {
|
|
||||||
props[key] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
props[attr.Key] = attr.Val
|
|
||||||
}
|
|
||||||
|
|
||||||
return parser.parseComponentProps(props)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (parser *TemplateParser) parseComponentProps(props map[string]string) (map[string]interface{}, error) {
|
|
||||||
result := map[string]interface{}{}
|
|
||||||
for key, val := range props {
|
|
||||||
if strings.HasPrefix(key, "...") {
|
|
||||||
values, _, err := parser.data.Exec(fmt.Sprintf("{{ %s }}", strings.TrimPrefix(key, "...")))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if values == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := values.(map[string]interface{}); ok {
|
|
||||||
for k := range values.(map[string]interface{}) {
|
|
||||||
result[k] = k
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
result[key] = val
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (parser *TemplateParser) componentFile(sel *goquery.Selection, props map[string]interface{}) (string, string, error) {
|
|
||||||
route := sel.AttrOr("is", "")
|
|
||||||
if route == "" {
|
|
||||||
return "", "", fmt.Errorf("Component route is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
data := Data{"$props": props}
|
|
||||||
route, _ = data.ReplaceUse(dataTokens, route)
|
|
||||||
route, _ = parser.data.Replace(route)
|
|
||||||
file := filepath.Join(string(os.PathSeparator), "public", parser.option.Root, route+".jit")
|
|
||||||
return file, route, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (locale *Locale) replaceVars(data Data) {
|
|
||||||
if locale.Keys != nil {
|
|
||||||
for key, val := range locale.Keys {
|
|
||||||
locale.Keys[key] = replaceRandVar(val, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if locale.Messages != nil {
|
|
||||||
for key, val := range locale.Messages {
|
|
||||||
locale.Messages[key] = replaceRandVar(val, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func componentWriter() {
|
func componentWriter() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
@ -351,110 +327,3 @@ func componentWriter() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getComponent(route string, file string, disableCache ...bool) (*JitComponent, error) {
|
|
||||||
|
|
||||||
noCache := false
|
|
||||||
if len(disableCache) > 0 && disableCache[0] {
|
|
||||||
noCache = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if comp, ok := Components[file]; ok && !noCache {
|
|
||||||
return comp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
comp, err := readComponent(route, file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the component to the cache
|
|
||||||
chComp <- &componentData{file, comp, saveComponent}
|
|
||||||
return comp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// readComponent read the JIT component
|
|
||||||
func readComponent(route string, file string) (*JitComponent, error) {
|
|
||||||
content, err := application.App.Read(file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the scripts
|
|
||||||
html := string(content)
|
|
||||||
rawScripts := ""
|
|
||||||
rawStyles := ""
|
|
||||||
rawOption := ""
|
|
||||||
html = reScripts.ReplaceAllStringFunc(html, func(exp string) string {
|
|
||||||
rawScripts = reScripts.ReplaceAllString(exp, "$1")
|
|
||||||
return ""
|
|
||||||
})
|
|
||||||
|
|
||||||
html = reStyles.ReplaceAllStringFunc(html, func(exp string) string {
|
|
||||||
rawStyles = reStyles.ReplaceAllString(exp, "$1")
|
|
||||||
return ""
|
|
||||||
})
|
|
||||||
|
|
||||||
html = reOption.ReplaceAllStringFunc(html, func(exp string) string {
|
|
||||||
rawOption = reOption.ReplaceAllString(exp, "$1")
|
|
||||||
return ""
|
|
||||||
})
|
|
||||||
|
|
||||||
scripts := []ScriptNode{}
|
|
||||||
styles := []StyleNode{}
|
|
||||||
buildOption := BuildOption{}
|
|
||||||
|
|
||||||
if rawScripts != "" {
|
|
||||||
err := jsoniter.UnmarshalFromString(rawScripts, &scripts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rawStyles != "" {
|
|
||||||
err := jsoniter.UnmarshalFromString(rawStyles, &styles)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rawOption != "" {
|
|
||||||
err := jsoniter.UnmarshalFromString(rawOption, &buildOption)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &JitComponent{
|
|
||||||
file: file,
|
|
||||||
route: route,
|
|
||||||
html: html,
|
|
||||||
scripts: scripts,
|
|
||||||
styles: styles,
|
|
||||||
buildOption: &buildOption,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func replaceRandVar(value string, data Data) string {
|
|
||||||
|
|
||||||
return propTokens.ReplaceAllStringFunc(value, func(exp string) string {
|
|
||||||
if strings.HasPrefix(exp, "[{") && strings.HasSuffix(exp, "}]") {
|
|
||||||
matches := propTokens.FindAllStringSubmatch(exp, -1)
|
|
||||||
if len(matches) > 0 {
|
|
||||||
identifiers, err := data.Identifiers(matches[0][1])
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
for _, identifier := range identifiers {
|
|
||||||
exp = strings.ReplaceAll(exp, identifier.Value, strings.ReplaceAll(identifier.Value, "$props.", ""))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
res := data.ExecString(exp)
|
|
||||||
if res.Error != nil {
|
|
||||||
return res.Error.Error()
|
|
||||||
}
|
|
||||||
return res.Value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -256,16 +256,17 @@ func (parser *TemplateParser) parseElementNode(sel *goquery.Selection) {
|
||||||
|
|
||||||
func (parser *TemplateParser) parseElementComponent(sel *goquery.Selection) {
|
func (parser *TemplateParser) parseElementComponent(sel *goquery.Selection) {
|
||||||
|
|
||||||
sel.SetAttr("parsed", "true")
|
parser.parsed(sel)
|
||||||
com := sel.AttrOr("s:cn", "")
|
com := sel.AttrOr("s:cn", "")
|
||||||
props := map[string]interface{}{}
|
props := map[string]interface{}{}
|
||||||
for _, attr := range sel.Nodes[0].Attr {
|
for _, attr := range sel.Nodes[0].Attr {
|
||||||
if !strings.HasPrefix(attr.Key, "s:") && attr.Key != "parsed" {
|
if strings.HasPrefix(attr.Key, "prop:") {
|
||||||
var val any = attr.Val
|
var val any = attr.Val
|
||||||
|
var key = strings.TrimPrefix(attr.Key, "prop:")
|
||||||
if _, exist := sel.Attr("json-attr-" + attr.Key); exist {
|
if _, exist := sel.Attr("json-attr-" + attr.Key); exist {
|
||||||
val = ValueJSON(attr.Val)
|
val = ValueJSON(attr.Val)
|
||||||
}
|
}
|
||||||
props[attr.Key] = val
|
props[key] = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -299,6 +300,11 @@ func (parser *TemplateParser) parseElementComponent(sel *goquery.Selection) {
|
||||||
compParser.data[k] = v
|
compParser.data[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
raw, err := jsoniter.MarshalToString(data)
|
||||||
|
if err != nil {
|
||||||
|
raw = fmt.Sprintf(`"%s"`, err.Error())
|
||||||
|
}
|
||||||
|
sel.SetAttr("json:__component_data", raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = compParser.RenderSelection(sel)
|
err = compParser.RenderSelection(sel)
|
||||||
|
|
@ -307,6 +313,7 @@ func (parser *TemplateParser) parseElementComponent(sel *goquery.Selection) {
|
||||||
setError(sel, err)
|
setError(sel, err)
|
||||||
}
|
}
|
||||||
parser.sequence = compParser.sequence + 1
|
parser.sequence = compParser.sequence + 1
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *TemplateParser) clone(script *Script) *TemplateParser {
|
func (parser *TemplateParser) clone(script *Script) *TemplateParser {
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,11 @@ func (r *Request) ExecString(data string) (Data, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r.Exec(res)
|
err = r.Exec(res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -288,7 +292,11 @@ func (r *Request) call(p interface{}) (interface{}, error) {
|
||||||
// Call the backend script
|
// Call the backend script
|
||||||
if r.Script != nil && strings.HasPrefix(processName, "@") {
|
if r.Script != nil && strings.HasPrefix(processName, "@") {
|
||||||
method := processName[1:]
|
method := processName[1:]
|
||||||
return r.Script.Call(r, method, processArgs...)
|
v, err := r.Script.Call(r, method, processArgs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("backend script %s %s, please check the script", method, err.Error())
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
process, err := process.Of(processName, processArgs...)
|
process, err := process.Of(processName, processArgs...)
|
||||||
|
|
|
||||||
|
|
@ -54,11 +54,11 @@ func scriptWriter() {
|
||||||
// LoadScript load the script
|
// LoadScript load the script
|
||||||
func LoadScript(file string) (*Script, error) {
|
func LoadScript(file string) (*Script, error) {
|
||||||
|
|
||||||
if script, has := Scripts[file]; has {
|
base := strings.TrimSuffix(strings.TrimSuffix(file, ".sui"), ".jit")
|
||||||
|
if script, has := Scripts[base]; has {
|
||||||
return script, nil
|
return script, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
base := strings.TrimSuffix(file, ".sui")
|
|
||||||
file = base + ".ts"
|
file = base + ".ts"
|
||||||
if exist, _ := application.App.Exists(file); !exist {
|
if exist, _ := application.App.Exists(file); !exist {
|
||||||
file = base + ".js"
|
file = base + ".js"
|
||||||
|
|
@ -79,7 +79,7 @@ func LoadScript(file string) (*Script, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
script := &Script{Script: v8script}
|
script := &Script{Script: v8script}
|
||||||
chScript <- &scriptData{file, script, saveScript}
|
chScript <- &scriptData{base, script, saveScript}
|
||||||
return script, nil
|
return script, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue