feat: output warning messages during sui page build

This commit is contained in:
Max 2024-07-09 05:56:29 +08:00
parent 9bae50b500
commit 8dd5f02a7b
10 changed files with 123 additions and 66 deletions

View file

@ -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))
},

View file

@ -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())

View file

@ -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 {

View file

@ -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
}

View file

@ -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() {

View file

@ -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("<div>" + html + "</div>")
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

View file

@ -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(`<script name="scripts" type="json">%s</script>`+"\n", rawScripts))
body.Children().First().AppendHtml(fmt.Sprintf(`<script name="styles" type="json">%s</script>`+"\n", rawStyles))
body.Children().First().AppendHtml(fmt.Sprintf(`<script name="option" type="json">%s</script>`+"\n", rawOption))
return body.Html()
html, err := body.Html()
return html, warnings, err
}
// CompileJS compile the javascript

View file

@ -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

View file

@ -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 <route> -> <name>.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 {

View file

@ -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), `<script name="config" type="json">`)
assert.Contains(t, string(content), `<script name="data" type="json">`)
assert.Contains(t, string(content), `<script name="global" type="json">`)
assert.Len(t, warnings, 0)
}
@ -70,7 +71,7 @@ func TestTemplateBuildAsComponent(t *testing.T) {
t.Fatalf("RemoveAll error: %v", err)
}
err = tmpl.Build(&core.BuildOption{SSR: true})
warnings, err := tmpl.Build(&core.BuildOption{SSR: true})
if err != nil {
t.Fatalf("Components error: %v", err)
}
@ -81,6 +82,7 @@ func TestTemplateBuildAsComponent(t *testing.T) {
// Check JIT
assert.FileExists(t, filepath.Join(path, cselect))
assert.FileExists(t, filepath.Join(path, cinput))
assert.Len(t, warnings, 0)
content, err := os.ReadFile(filepath.Join(path, cselect))
if err != nil {
@ -122,7 +124,7 @@ func TestPageBuild(t *testing.T) {
t.Fatalf("Page error: %v", err)
}
err = page.Build(nil, &core.BuildOption{SSR: true, AssetRoot: "/unit-test/assets"})
warnings, err := page.Build(nil, &core.BuildOption{SSR: true, AssetRoot: "/unit-test/assets"})
if err != nil {
t.Fatalf("Page Build error: %v", err)
}
@ -141,6 +143,7 @@ func TestPageBuild(t *testing.T) {
assert.Contains(t, string(content), `<script name="config" type="json">`)
assert.Contains(t, string(content), `<script name="data" type="json">`)
assert.Contains(t, string(content), `<script name="global" type="json">`)
assert.Len(t, warnings, 0)
}
func TestPageBuildAsComponent(t *testing.T) {
@ -167,10 +170,11 @@ func TestPageBuildAsComponent(t *testing.T) {
t.Fatalf("Page error: %v", err)
}
err = page.Build(nil, &core.BuildOption{SSR: true})
warnings, err := page.Build(nil, &core.BuildOption{SSR: true})
if err != nil {
t.Fatalf("Components error: %v", err)
}
assert.Len(t, warnings, 0)
cselect := "/flowbite/components/edit/select.jit"
cinput := "/flowbite/components/edit/input.jit"