From 8dd5f02a7b8612a0dc7455d612c15800960efca5 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 9 Jul 2024 05:56:29 +0800 Subject: [PATCH] feat: output warning messages during sui page build --- cmd/sui/build.go | 7 ++- cmd/sui/watch.go | 8 +++- sui/api/compile_test.go | 3 +- sui/api/process.go | 10 +++- sui/api/sui_test.go | 3 +- sui/core/build.go | 34 +++++++++----- sui/core/compile.go | 26 ++++++----- sui/core/interfaces.go | 6 +-- sui/storages/local/build.go | 80 ++++++++++++++++++++------------ sui/storages/local/build_test.go | 12 +++-- 10 files changed, 123 insertions(+), 66 deletions(-) diff --git a/cmd/sui/build.go b/cmd/sui/build.go index 3ff94266..795e308d 100644 --- a/cmd/sui/build.go +++ b/cmd/sui/build.go @@ -88,7 +88,7 @@ var BuildCmd = &cobra.Command{ mode = "development" } - err = tmpl.Build(&core.BuildOption{SSR: true, AssetRoot: assetRoot, ExecScripts: true, ScriptMinify: minify, StyleMinify: minify}) + warnings, err := tmpl.Build(&core.BuildOption{SSR: true, AssetRoot: assetRoot, ExecScripts: true, ScriptMinify: minify, StyleMinify: minify}) if err != nil { fmt.Fprintln(os.Stderr, color.RedString(err.Error())) return @@ -99,6 +99,11 @@ var BuildCmd = &cobra.Command{ fmt.Println(color.YellowString("Build succeeded for %s in %s", mode, timecost)) return } + if len(warnings) > 0 { + for _, warning := range warnings { + fmt.Println(color.YellowString("Warning: %s", warning)) + } + } fmt.Println(color.GreenString("Build succeeded for %s in %s", mode, timecost)) }, diff --git a/cmd/sui/watch.go b/cmd/sui/watch.go index 2b738309..c599d3e6 100644 --- a/cmd/sui/watch.go +++ b/cmd/sui/watch.go @@ -98,11 +98,17 @@ var WatchCmd = &cobra.Command{ // Timecost start := time.Now() - err = tmpl.Build(&core.BuildOption{SSR: true, AssetRoot: assetRoot}) + warnings, err := tmpl.Build(&core.BuildOption{SSR: true, AssetRoot: assetRoot}) if err != nil { fmt.Fprint(os.Stderr, color.RedString(fmt.Sprintf("Failed: %s\n", err.Error()))) return } + + if len(warnings) > 0 { + for _, warning := range warnings { + fmt.Fprintln(os.Stderr, color.YellowString(warning)) + } + } end := time.Now() timecost := end.Sub(start).Truncate(time.Millisecond) fmt.Printf(color.GreenString("Success (%s)\n"), timecost.String()) diff --git a/sui/api/compile_test.go b/sui/api/compile_test.go index 2434d1ec..02e9dccf 100644 --- a/sui/api/compile_test.go +++ b/sui/api/compile_test.go @@ -12,11 +12,12 @@ func TestCompile(t *testing.T) { defer clean() page := testPage(t) - html, err := page.Compile(nil, &core.BuildOption{KeepPageTag: false}) + html, warnings, err := page.Compile(nil, &core.BuildOption{KeepPageTag: false}) if err != nil { t.Fatalf("Compile error: %v", err) } assert.Contains(t, html, `The basic test cases`) + assert.Len(t, warnings, 0) } func testPage(t *testing.T) *core.Page { diff --git a/sui/api/process.go b/sui/api/process.go index 56c7639b..ab572a3c 100644 --- a/sui/api/process.go +++ b/sui/api/process.go @@ -963,11 +963,14 @@ func BuildAll(process *process.Process) interface{} { exception.New(err.Error(), 500).Throw() } - err = tmpl.Build(&core.BuildOption{SSR: ssr, AssetRoot: assetRoot, Data: data}) + warnings, err := tmpl.Build(&core.BuildOption{SSR: ssr, AssetRoot: assetRoot, Data: data}) if err != nil { exception.New(err.Error(), 500).Throw() } + if warnings != nil && len(warnings) > 0 { + return warnings + } return nil } @@ -1004,10 +1007,13 @@ func BuildPage(process *process.Process) interface{} { } data := process.ArgsMap(5, map[string]interface{}{}) - err = page.Build(nil, &core.BuildOption{SSR: ssr, AssetRoot: assetRoot, Data: data}) + warnings, err := page.Build(nil, &core.BuildOption{SSR: ssr, AssetRoot: assetRoot, Data: data}) if err != nil { exception.New(err.Error(), 500).Throw() } + if warnings != nil && len(warnings) > 0 { + return warnings + } return nil } diff --git a/sui/api/sui_test.go b/sui/api/sui_test.go index aba20f8b..24cea679 100644 --- a/sui/api/sui_test.go +++ b/sui/api/sui_test.go @@ -37,10 +37,11 @@ func prepare(t *testing.T) { t.Fatal(err) } - err = advanced.Build(&core.BuildOption{SSR: true, AssetRoot: "/unit-test/assets"}) + warnings, err := advanced.Build(&core.BuildOption{SSR: true, AssetRoot: "/unit-test/assets"}) if err != nil { t.Fatal(err) } + assert.Len(t, warnings, 0) } func clean() { diff --git a/sui/core/build.go b/sui/core/build.go index 68c45886..372108d1 100644 --- a/sui/core/build.go +++ b/sui/core/build.go @@ -9,7 +9,6 @@ import ( "github.com/PuerkitoBio/goquery" "github.com/fatih/color" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/kun/log" "golang.org/x/net/html" ) @@ -54,10 +53,13 @@ func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Docume } doc.Find("body").SetAttr("s:ns", namespace) - err = page.buildComponents(doc, ctx, option) + warnings, err := page.buildComponents(doc, ctx, option) if err != nil { return nil, ctx.warnings, err } + if warnings != nil && len(warnings) > 0 { + ctx.warnings = append(ctx.warnings, warnings...) + } // Scripts scripts, err := page.BuildScripts(ctx, option, "__page", namespace) @@ -127,8 +129,13 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op } body := doc.Selection.Find("body") - if body.Length() > 1 { - body.SetHtml("
" + html + "
") + + if body.Children().Length() == 0 { + return "", fmt.Errorf("page %s as component should have one root element", page.Route) + } + + if body.Children().Length() > 1 { + return "", fmt.Errorf("page %s as component should have only one root element", page.Route) } // Scripts @@ -241,16 +248,17 @@ func (page *Page) copyProps(ctx *BuildContext, from *goquery.Selection, to *goqu return nil } -func (page *Page) buildComponents(doc *goquery.Document, ctx *BuildContext, option *BuildOption) error { +func (page *Page) buildComponents(doc *goquery.Document, ctx *BuildContext, option *BuildOption) ([]string, error) { + warnings := []string{} sui := SUIs[page.SuiID] if sui == nil { - return fmt.Errorf("SUI %s not found", page.SuiID) + return warnings, fmt.Errorf("SUI %s not found", page.SuiID) } public := sui.GetPublic() tmpl, err := sui.GetTemplate(page.TemplateID) if err != nil { - return err + return warnings, err } doc.Find("*").Each(func(i int, sel *goquery.Selection) { @@ -279,15 +287,17 @@ func (page *Page) buildComponents(doc *goquery.Document, ctx *BuildContext, opti sel.SetAttr("parsed", "true") ipage, err := tmpl.Page(name) if err != nil { + message := err.Error() + warnings = append(warnings, message) 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 { + message := err.Error() + warnings = append(warnings, message) setError(sel, err) - log.Warn("Page %s/%s/%s: %s", page.SuiID, page.TemplateID, page.Route, err.Error()) return } @@ -295,14 +305,14 @@ func (page *Page) buildComponents(doc *goquery.Document, ctx *BuildContext, opti component.parent = page _, err = component.BuildAsComponent(sel, ctx, option) if err != nil { + message := err.Error() + warnings = append(warnings, message) setError(sel, err) - log.Warn("Page %s/%s/%s: %s", page.SuiID, page.TemplateID, page.Route, err.Error()) return } - return }) - return err + return warnings, nil } // BuildStyles build the styles for the page diff --git a/sui/core/compile.go b/sui/core/compile.go index 5feebe7c..6ab387c1 100644 --- a/sui/core/compile.go +++ b/sui/core/compile.go @@ -19,11 +19,11 @@ var importAssetsRe = regexp.MustCompile(`import\s*\t*\n*\s*['"]@assets\/([^'"]+) var AssetsRe = regexp.MustCompile(`[` + quoteRe + `]@assets\/([^` + quoteRe + `]+)[` + quoteRe + `]`) // '@assets/foo.js' or "@assets/foo.js" or `@assets/foo` // Compile the page -func (page *Page) Compile(ctx *BuildContext, option *BuildOption) (string, error) { +func (page *Page) Compile(ctx *BuildContext, option *BuildOption) (string, []string, error) { doc, warnings, err := page.Build(ctx, option) if err != nil { - return "", err + return "", warnings, err } if warnings != nil && len(warnings) > 0 { @@ -88,22 +88,22 @@ func (page *Page) Compile(ctx *BuildContext, option *BuildOption) (string, error page.ReplaceDocument(doc) html, err := doc.Html() if err != nil { - return "", err + return "", warnings, err } // @todo: Minify the html - return html, nil + return html, warnings, nil } // CompileAsComponent compile the page as component -func (page *Page) CompileAsComponent(ctx *BuildContext, option *BuildOption) (string, error) { +func (page *Page) CompileAsComponent(ctx *BuildContext, option *BuildOption) (string, []string, error) { opt := *option opt.IgnoreDocument = true opt.WithWrapper = true doc, warnings, err := page.Build(ctx, &opt) if err != nil { - return "", err + return "", warnings, err } if warnings != nil && len(warnings) > 0 { @@ -115,31 +115,33 @@ func (page *Page) CompileAsComponent(ctx *BuildContext, option *BuildOption) (st body := doc.Find("body") rawScripts, err := jsoniter.MarshalToString(ctx.scripts) if err != nil { - return "", err + return "", warnings, err } rawStyles, err := jsoniter.MarshalToString(ctx.styles) if err != nil { - return "", err + return "", warnings, err } rawOption, err := jsoniter.MarshalToString(option) if err != nil { - return "", err + return "", warnings, err } if body.Children().Length() == 0 { - return "", 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) } if body.Children().Length() > 1 { - return "", fmt.Errorf("page %s as component should have only one root element", page.Route) + return "", warnings, fmt.Errorf("page %s as component should have only one root element", page.Route) } body.Children().First().AppendHtml(fmt.Sprintf(``+"\n", rawScripts)) body.Children().First().AppendHtml(fmt.Sprintf(``+"\n", rawStyles)) body.Children().First().AppendHtml(fmt.Sprintf(``+"\n", rawOption)) - return body.Html() + + html, err := body.Html() + return html, warnings, err } // CompileJS compile the javascript diff --git a/sui/core/interfaces.go b/sui/core/interfaces.go index 795869e3..2f54d8d4 100644 --- a/sui/core/interfaces.go +++ b/sui/core/interfaces.go @@ -58,7 +58,7 @@ type ITemplate interface { MediaSearch(query url.Values, page int, pageSize int) (MediaSearchResult, error) - Build(option *BuildOption) error + Build(option *BuildOption) ([]string, error) SyncAssets(option *BuildOption) error SyncAssetFile(file string, option *BuildOption) error GetRoot() string @@ -92,8 +92,8 @@ type IPage interface { AssetScript() (*Asset, error) AssetStyle() (*Asset, error) - Build(globalCtx *GlobalBuildContext, option *BuildOption) error - BuildAsComponent(globalCtx *GlobalBuildContext, option *BuildOption) error + Build(globalCtx *GlobalBuildContext, option *BuildOption) ([]string, error) + BuildAsComponent(globalCtx *GlobalBuildContext, option *BuildOption) ([]string, error) } // IBlock is the interface for the block diff --git a/sui/storages/local/build.go b/sui/storages/local/build.go index f1d739e0..abd7d892 100644 --- a/sui/storages/local/build.go +++ b/sui/storages/local/build.go @@ -14,8 +14,9 @@ import ( ) // Build the template -func (tmpl *Template) Build(option *core.BuildOption) error { +func (tmpl *Template) Build(option *core.BuildOption) ([]string, error) { var err error + warnings := []string{} defer func() { if option.ExecScripts { tmpl.ExecBuildCompleteScripts() @@ -32,12 +33,12 @@ func (tmpl *Template) Build(option *core.BuildOption) error { } } if len(scriptsErrorMessages) > 0 { - return fmt.Errorf("Build scripts error: %s", strings.Join(scriptsErrorMessages, ";\n")) + return warnings, fmt.Errorf("Build scripts error: %s", strings.Join(scriptsErrorMessages, ";\n")) } err = tmpl.Reload() if err != nil { - return err + return warnings, err } } @@ -53,14 +54,14 @@ func (tmpl *Template) Build(option *core.BuildOption) error { // Sync the assets if err = tmpl.SyncAssets(option); err != nil { - return err + return warnings, err } // Build all pages ctx := core.NewGlobalBuildContext() pages, err := tmpl.Pages() if err != nil { - return err + return warnings, err } // loaed pages @@ -68,20 +69,25 @@ func (tmpl *Template) Build(option *core.BuildOption) error { for _, page := range pages { err := page.Load() if err != nil { - return err + return warnings, err } - err = page.Build(ctx, option) + messages, err := page.Build(ctx, option) if err != nil { - return err + return warnings, err } + + if len(messages) > 0 { + warnings = append(warnings, messages...) + } + tmpl.loaded[page.Get().Route] = page } // Build jit components for the global -> .sui.lib jitComponents, err := tmpl.GlobRoutes(ctx.GetJitComponents(), true) if err != nil { - return err + return warnings, err } for _, route := range jitComponents { @@ -90,14 +96,18 @@ func (tmpl *Template) Build(option *core.BuildOption) error { err = multierror.Append(fmt.Errorf("The page %s is not loaded", route)) continue } - err = page.BuildAsComponent(ctx, option) + + messages, err := page.BuildAsComponent(ctx, option) if err != nil { err = multierror.Append(err) } + if len(messages) > 0 { + warnings = append(warnings, messages...) + } } if err != nil { - return err + return warnings, err } // Execute the build after hook @@ -110,11 +120,11 @@ func (tmpl *Template) Build(option *core.BuildOption) error { } } if len(scriptsErrorMessages) > 0 { - return fmt.Errorf("Build scripts error: %s", strings.Join(scriptsErrorMessages, ";\n")) + return warnings, fmt.Errorf("Build scripts error: %s", strings.Join(scriptsErrorMessages, ";\n")) } } - return err + return warnings, err } // SyncAssetFile sync the assets @@ -171,7 +181,7 @@ func (tmpl *Template) SyncAssets(option *core.BuildOption) error { } // Build is the struct for the public -func (page *Page) Build(globalCtx *core.GlobalBuildContext, option *core.BuildOption) error { +func (page *Page) Build(globalCtx *core.GlobalBuildContext, option *core.BuildOption) ([]string, error) { ctx := core.NewBuildContext(globalCtx) if option.AssetRoot == "" { @@ -183,28 +193,28 @@ func (page *Page) Build(globalCtx *core.GlobalBuildContext, option *core.BuildOp option.AssetRoot = filepath.Join(root, "assets") } - html, err := page.Page.Compile(ctx, option) + html, warnings, err := page.Page.Compile(ctx, option) if err != nil { - return err + return warnings, err } // Save the html err = page.writeHTML([]byte(html), option.Data) if err != nil { - return err + return warnings, err } // Save the locale files err = page.writeLocaleFiles(option.Data) if err != nil { - return err + return warnings, err } // Jit Components if globalCtx == nil { jitComponents, err := page.tmpl.GlobRoutes(ctx.GetJitComponents(), true) if err != nil { - return err + return warnings, err } for _, route := range jitComponents { @@ -218,20 +228,24 @@ func (page *Page) Build(globalCtx *core.GlobalBuildContext, option *core.BuildOp } } - err = p.BuildAsComponent(globalCtx, option) + messages, err := p.BuildAsComponent(globalCtx, option) if err != nil { err = multierror.Append(err) } + if len(messages) > 0 { + warnings = append(warnings, messages...) + } } } - return err + return warnings, err } // BuildAsComponent build the page as component -func (page *Page) BuildAsComponent(globalCtx *core.GlobalBuildContext, option *core.BuildOption) error { +func (page *Page) BuildAsComponent(globalCtx *core.GlobalBuildContext, option *core.BuildOption) ([]string, error) { + warnings := []string{} ctx := core.NewBuildContext(globalCtx) if option.AssetRoot == "" { root, err := page.tmpl.local.DSL.PublicRoot(option.Data) @@ -242,28 +256,32 @@ func (page *Page) BuildAsComponent(globalCtx *core.GlobalBuildContext, option *c option.AssetRoot = filepath.Join(root, "assets") } - html, err := page.Page.CompileAsComponent(ctx, option) + html, messages, err := page.Page.CompileAsComponent(ctx, option) if err != nil { - return err + return warnings, err + } + + if len(messages) > 0 { + warnings = append(warnings, messages...) } // Save the html err = page.writeJitHTML([]byte(html), option.Data) if err != nil { - return err + return warnings, err } // Save the locale files err = page.writeLocaleFiles(option.Data) if err != nil { - return err + return warnings, err } // Jit Components if globalCtx == nil { jitComponents, err := page.tmpl.GlobRoutes(ctx.GetJitComponents(), true) if err != nil { - return err + return warnings, err } for _, route := range jitComponents { @@ -277,14 +295,18 @@ func (page *Page) BuildAsComponent(globalCtx *core.GlobalBuildContext, option *c } } - err = p.BuildAsComponent(globalCtx, option) + messages, err := p.BuildAsComponent(globalCtx, option) if err != nil { err = multierror.Append(err) } + + if len(messages) > 0 { + warnings = append(warnings, messages...) + } } } - return err + return warnings, err } func (page *Page) publicFile(data map[string]interface{}) string { diff --git a/sui/storages/local/build_test.go b/sui/storages/local/build_test.go index c2d939f3..31f7a0ac 100644 --- a/sui/storages/local/build_test.go +++ b/sui/storages/local/build_test.go @@ -29,7 +29,7 @@ func TestTemplateBuild(t *testing.T) { t.Fatalf("RemoveAll error: %v", err) } - err = tmpl.Build(&core.BuildOption{SSR: true, ExecScripts: true}) + warnings, err := tmpl.Build(&core.BuildOption{SSR: true, ExecScripts: true}) if err != nil { t.Fatalf("Components error: %v", err) } @@ -48,6 +48,7 @@ func TestTemplateBuild(t *testing.T) { assert.Contains(t, string(content), `