From 3f95dd7d75340fd3a8e8ec821d60734e8a9970db Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 7 Jul 2024 01:19:15 +0800 Subject: [PATCH] [add] sui recursive build detected --- sui/core/build.go | 44 +++++++++++++++++++++++++++++++++++++++++--- sui/core/context.go | 2 ++ sui/core/types.go | 2 ++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/sui/core/build.go b/sui/core/build.go index 1aa2a4695..fabfe6cf5 100644 --- a/sui/core/build.go +++ b/sui/core/build.go @@ -25,6 +25,19 @@ func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Docume ctx = NewBuildContext(nil) } + // Push the current page onto the stack and increment the visit counter + ctx.stack = append(ctx.stack, page.Route) + ctx.visited[page.Route]++ + defer func() { + ctx.stack = ctx.stack[:len(ctx.stack)-1] // Pop the stack + ctx.visited[page.Route]-- + }() + + // Check for recursive calls + if ctx.visited[page.Route] > 1 { + return nil, ctx.warnings, fmt.Errorf("recursive build detected for page %s", page.Route) + } + ctx.sequence++ html, err := page.BuildHTML(option) if err != nil { @@ -68,6 +81,19 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op return "", fmt.Errorf("The parent page is not set") } + // Push the current page onto the stack and increment the visit counter + ctx.stack = append(ctx.stack, page.Route) + ctx.visited[page.Route]++ + defer func() { + ctx.stack = ctx.stack[:len(ctx.stack)-1] // Pop the stack + ctx.visited[page.Route]-- + }() + + // Check for recursive calls + if ctx.visited[page.Route] > 1 { + return "", fmt.Errorf("recursive build detected for page %s", page.Route) + } + name, exists := sel.Attr("is") if !exists { return "", fmt.Errorf("The component tag must have an is attribute") @@ -148,6 +174,7 @@ func (page *Page) copySlots(ctx *BuildContext, from *goquery.Selection, to *goqu html, err := slot.Html() if err != nil { ctx.warnings = append(ctx.warnings, err.Error()) + setError(slotSel, err) continue } slotSel.SetHtml(html) @@ -172,6 +199,7 @@ func (page *Page) copyProps(ctx *BuildContext, from *goquery.Selection, to *goqu 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) { @@ -245,21 +273,26 @@ func (page *Page) buildComponents(doc *goquery.Document, ctx *BuildContext, opti ipage, err := tmpl.Page(name) if err != nil { - sel.ReplaceWith(fmt.Sprintf("", err.Error())) + setError(sel, err) log.Warn("Page %s/%s/%s: %s", page.SuiID, page.TemplateID, page.Route, err.Error()) return } err = ipage.Load() if err != nil { - sel.ReplaceWith(fmt.Sprintf("", err.Error())) + setError(sel, err) log.Warn("Page %s/%s/%s: %s", page.SuiID, page.TemplateID, page.Route, err.Error()) return } component := ipage.Get() component.parent = page - component.BuildAsComponent(sel, ctx, option) + _, err = component.BuildAsComponent(sel, ctx, option) + if err != nil { + setError(sel, err) + log.Warn("Page %s/%s/%s: %s", page.SuiID, page.TemplateID, page.Route, err.Error()) + return + } return }) @@ -424,6 +457,11 @@ func (page *Page) BuildHTML(option *BuildOption) (string, error) { return string(res), nil } +func setError(sel *goquery.Selection, err error) { + html := `
%s
` + sel.SetHtml(fmt.Sprintf(html, err.Error())) +} + func addTabToEachLine(input string, prefix ...string) string { var lines []string diff --git a/sui/core/context.go b/sui/core/context.go index ed0883f61..36233a389 100644 --- a/sui/core/context.go +++ b/sui/core/context.go @@ -12,6 +12,8 @@ func NewBuildContext(global *GlobalBuildContext) *BuildContext { jitComponents: map[string]bool{}, global: global, warnings: []string{}, + visited: map[string]int{}, + stack: []string{}, } } diff --git a/sui/core/types.go b/sui/core/types.go index 47add5c50..6eaee9a10 100644 --- a/sui/core/types.go +++ b/sui/core/types.go @@ -58,6 +58,8 @@ type BuildContext struct { global *GlobalBuildContext translations []Translation warnings []string + visited map[string]int // Keep a counter for each page + stack []string // Stack to manage build states } // ScriptNode is the struct for the script node