Merge pull request #639 from trheyi/main
[add] Support for JIT mode in SUI component imports ( dev )
This commit is contained in:
commit
ad5bf8460d
12 changed files with 208 additions and 29 deletions
|
|
@ -13,7 +13,7 @@ func TestCompile(t *testing.T) {
|
||||||
loadTestSui(t)
|
loadTestSui(t)
|
||||||
|
|
||||||
page := testPage(t)
|
page := testPage(t)
|
||||||
html, err := page.Compile(&core.BuildOption{KeepPageTag: false})
|
html, err := page.Compile(nil, &core.BuildOption{KeepPageTag: false})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Compile error: %v", err)
|
t.Fatalf("Compile error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1004,7 +1004,7 @@ func BuildPage(process *process.Process) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
data := process.ArgsMap(5, map[string]interface{}{})
|
data := process.ArgsMap(5, map[string]interface{}{})
|
||||||
err = page.Build(&core.BuildOption{SSR: ssr, AssetRoot: assetRoot, Data: data})
|
err = page.Build(nil, &core.BuildOption{SSR: ssr, AssetRoot: assetRoot, Data: data})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exception.New(err.Error(), 500).Throw()
|
exception.New(err.Error(), 500).Throw()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,6 @@ var cssRe = regexp.MustCompile(`([\.a-z0-9A-Z-:# ]+)\{`)
|
||||||
var langFuncRe = regexp.MustCompile(`L\s*\(\s*["'](.*?)["']\s*\)`)
|
var langFuncRe = regexp.MustCompile(`L\s*\(\s*["'](.*?)["']\s*\)`)
|
||||||
var langAttrRe = regexp.MustCompile(`'::(.*?)'`)
|
var langAttrRe = regexp.MustCompile(`'::(.*?)'`)
|
||||||
|
|
||||||
// NewBuildContext create a new build context
|
|
||||||
func NewBuildContext() *BuildContext {
|
|
||||||
return &BuildContext{
|
|
||||||
components: map[string]bool{},
|
|
||||||
sequence: 1,
|
|
||||||
scripts: []string{},
|
|
||||||
styles: []string{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build is the struct for the public
|
// Build is the struct for the public
|
||||||
func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Document, []string, error) {
|
func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Document, []string, error) {
|
||||||
|
|
||||||
|
|
@ -148,6 +138,7 @@ func (page *Page) BuildForImport(ctx *BuildContext, option *BuildOption, slots m
|
||||||
|
|
||||||
func (page *Page) parse(ctx *BuildContext, doc *goquery.Document, option *BuildOption, warnings []string) error {
|
func (page *Page) parse(ctx *BuildContext, doc *goquery.Document, option *BuildOption, warnings []string) error {
|
||||||
|
|
||||||
|
// Find the import pages
|
||||||
pages := doc.Find("*").FilterFunction(func(i int, sel *goquery.Selection) bool {
|
pages := doc.Find("*").FilterFunction(func(i int, sel *goquery.Selection) bool {
|
||||||
|
|
||||||
// Get the translation
|
// Get the translation
|
||||||
|
|
@ -164,8 +155,12 @@ func (page *Page) parse(ctx *BuildContext, doc *goquery.Document, option *BuildO
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
_, has := sel.Attr("is")
|
name, has := sel.Attr("is")
|
||||||
return has
|
_, jit := sel.Attr("s:jit") // Just in time rendering
|
||||||
|
if has && jit {
|
||||||
|
ctx.addJitComponent(name)
|
||||||
|
}
|
||||||
|
return has && !jit
|
||||||
})
|
})
|
||||||
|
|
||||||
sui := SUIs[page.SuiID]
|
sui := SUIs[page.SuiID]
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,8 @@ 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`
|
var AssetsRe = regexp.MustCompile(`[` + quoteRe + `]@assets\/([^` + quoteRe + `]+)[` + quoteRe + `]`) // '@assets/foo.js' or "@assets/foo.js" or `@assets/foo`
|
||||||
|
|
||||||
// Compile the page
|
// Compile the page
|
||||||
func (page *Page) Compile(option *BuildOption) (string, error) {
|
func (page *Page) Compile(ctx *BuildContext, option *BuildOption) (string, error) {
|
||||||
|
|
||||||
ctx := NewBuildContext()
|
|
||||||
doc, warnings, err := page.Build(ctx, option)
|
doc, warnings, err := page.Build(ctx, option)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
|
||||||
54
sui/core/context.go
Normal file
54
sui/core/context.go
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
// NewBuildContext create a new build context
|
||||||
|
func NewBuildContext(global *GlobalBuildContext) *BuildContext {
|
||||||
|
return &BuildContext{
|
||||||
|
components: map[string]bool{},
|
||||||
|
sequence: 1,
|
||||||
|
scripts: []string{},
|
||||||
|
styles: []string{},
|
||||||
|
jitComponents: map[string]bool{},
|
||||||
|
global: global,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGlobalBuildContext create a new global build context
|
||||||
|
func NewGlobalBuildContext() *GlobalBuildContext {
|
||||||
|
return &GlobalBuildContext{
|
||||||
|
jitComponents: map[string]bool{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetJitComponents get the just in time components
|
||||||
|
func (ctx *BuildContext) GetJitComponents() []string {
|
||||||
|
if ctx.jitComponents == nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
jitComponents := []string{}
|
||||||
|
for name := range ctx.jitComponents {
|
||||||
|
jitComponents = append(jitComponents, name)
|
||||||
|
}
|
||||||
|
return jitComponents
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetJitComponents get the just in time components
|
||||||
|
func (globalCtx *GlobalBuildContext) GetJitComponents() []string {
|
||||||
|
if globalCtx.jitComponents == nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
jitComponents := []string{}
|
||||||
|
for name := range globalCtx.jitComponents {
|
||||||
|
jitComponents = append(jitComponents, name)
|
||||||
|
}
|
||||||
|
return jitComponents
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *BuildContext) addJitComponent(name string) {
|
||||||
|
name = stmtRe.ReplaceAllString(name, "*")
|
||||||
|
name = propRe.ReplaceAllString(name, "*")
|
||||||
|
ctx.jitComponents[name] = true
|
||||||
|
if ctx.global != nil {
|
||||||
|
ctx.global.jitComponents[name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -48,7 +48,7 @@ func (page *Page) EditorRender() (*ResponseEditorRender, error) {
|
||||||
// Render tools
|
// Render tools
|
||||||
// res.Scripts = append(res.Scripts, filepath.Join("@assets", "__render.js"))
|
// res.Scripts = append(res.Scripts, filepath.Join("@assets", "__render.js"))
|
||||||
// res.Styles = append(res.Styles, filepath.Join("@assets", "__render.css"))
|
// res.Styles = append(res.Styles, filepath.Join("@assets", "__render.css"))
|
||||||
ctx := NewBuildContext()
|
ctx := NewBuildContext(nil)
|
||||||
doc, warnings, err := page.Build(ctx, &BuildOption{
|
doc, warnings, err := page.Build(ctx, &BuildOption{
|
||||||
SSR: true,
|
SSR: true,
|
||||||
IgnoreAssetRoot: true,
|
IgnoreAssetRoot: true,
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,8 @@ type IPage interface {
|
||||||
AssetScript() (*Asset, error)
|
AssetScript() (*Asset, error)
|
||||||
AssetStyle() (*Asset, error)
|
AssetStyle() (*Asset, error)
|
||||||
|
|
||||||
Build(option *BuildOption) error
|
Build(globalCtx *GlobalBuildContext, option *BuildOption) error
|
||||||
|
BuildAsComponent(globalCtx *GlobalBuildContext, option *BuildOption) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// IBlock is the interface for the block
|
// IBlock is the interface for the block
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ func (page *Page) PreviewRender(referer string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
warnings := []string{}
|
warnings := []string{}
|
||||||
ctx := NewBuildContext()
|
ctx := NewBuildContext(nil)
|
||||||
doc, warnings, err := page.Build(ctx, &BuildOption{
|
doc, warnings, err := page.Build(ctx, &BuildOption{
|
||||||
SSR: true,
|
SSR: true,
|
||||||
AssetRoot: fmt.Sprintf("/api/__yao/sui/v1/%s/asset/%s/@assets", page.SuiID, page.TemplateID),
|
AssetRoot: fmt.Sprintf("/api/__yao/sui/v1/%s/asset/%s/@assets", page.SuiID, page.TemplateID),
|
||||||
|
|
|
||||||
|
|
@ -39,16 +39,25 @@ type Page struct {
|
||||||
Document []byte `json:"-"`
|
Document []byte `json:"-"`
|
||||||
GlobalData []byte `json:"-"`
|
GlobalData []byte `json:"-"`
|
||||||
Attrs map[string]string `json:"-"`
|
Attrs map[string]string `json:"-"`
|
||||||
Translations []Translation `json:"-"`
|
Translations []Translation `json:"-"` // will be deprecated
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildContext is the struct for the build context
|
// BuildContext is the struct for the build context
|
||||||
type BuildContext struct {
|
type BuildContext struct {
|
||||||
components map[string]bool
|
components map[string]bool
|
||||||
|
jitComponents map[string]bool
|
||||||
sequence int
|
sequence int
|
||||||
doc *goquery.Document
|
doc *goquery.Document
|
||||||
scripts []string
|
scripts []string
|
||||||
styles []string
|
styles []string
|
||||||
|
global *GlobalBuildContext
|
||||||
|
translations []Translation
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalBuildContext is the struct for the global build context
|
||||||
|
type GlobalBuildContext struct {
|
||||||
|
jitComponents map[string]bool
|
||||||
|
tmpl ITemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translation is the struct for the translation
|
// Translation is the struct for the translation
|
||||||
|
|
|
||||||
|
|
@ -52,11 +52,14 @@ func (tmpl *Template) Build(option *core.BuildOption) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build all pages
|
// Build all pages
|
||||||
|
ctx := core.NewGlobalBuildContext()
|
||||||
pages, err := tmpl.Pages()
|
pages, err := tmpl.Pages()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loaed pages
|
||||||
|
tmpl.loaded = map[string]core.IPage{}
|
||||||
for _, page := range pages {
|
for _, page := range pages {
|
||||||
perr := page.Load()
|
perr := page.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -64,9 +67,29 @@ func (tmpl *Template) Build(option *core.BuildOption) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
perr = page.Build(option)
|
perr = page.Build(ctx, option)
|
||||||
if perr != nil {
|
if perr != nil {
|
||||||
err = multierror.Append(perr)
|
err = multierror.Append(perr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, route := range jitComponents {
|
||||||
|
page, has := tmpl.loaded[route]
|
||||||
|
if !has {
|
||||||
|
err = multierror.Append(fmt.Errorf("The page %s is not loaded", route))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = page.BuildAsComponent(ctx, option)
|
||||||
|
if err != nil {
|
||||||
|
err = multierror.Append(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,8 +168,9 @@ func (tmpl *Template) SyncAssets(option *core.BuildOption) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build is the struct for the public
|
// Build is the struct for the public
|
||||||
func (page *Page) Build(option *core.BuildOption) error {
|
func (page *Page) Build(globalCtx *core.GlobalBuildContext, option *core.BuildOption) error {
|
||||||
|
|
||||||
|
ctx := core.NewBuildContext(globalCtx)
|
||||||
if option.AssetRoot == "" {
|
if option.AssetRoot == "" {
|
||||||
root, err := page.tmpl.local.DSL.PublicRoot(option.Data)
|
root, err := page.tmpl.local.DSL.PublicRoot(option.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -157,7 +181,7 @@ func (page *Page) Build(option *core.BuildOption) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("Build the page %s AssetRoot: %s", page.Route, option.AssetRoot)
|
log.Trace("Build the page %s AssetRoot: %s", page.Route, option.AssetRoot)
|
||||||
html, err := page.Page.Compile(option)
|
html, err := page.Page.Compile(ctx, option)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -169,7 +193,43 @@ func (page *Page) Build(option *core.BuildOption) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the locale files
|
// Save the locale files
|
||||||
return page.writeLocaleFiles(option.Data)
|
err = page.writeLocaleFiles(option.Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jit Components
|
||||||
|
if globalCtx == nil {
|
||||||
|
jitComponents, err := page.tmpl.GlobRoutes(ctx.GetJitComponents(), true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, route := range jitComponents {
|
||||||
|
var err error
|
||||||
|
p := page.tmpl.loaded[route]
|
||||||
|
if p == nil {
|
||||||
|
p, err = page.tmpl.Page(route)
|
||||||
|
if err != nil {
|
||||||
|
err = multierror.Append(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.BuildAsComponent(globalCtx, option)
|
||||||
|
if err != nil {
|
||||||
|
err = multierror.Append(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildAsComponent build the page as component
|
||||||
|
func (page *Page) BuildAsComponent(globalCtx *core.GlobalBuildContext, option *core.BuildOption) error {
|
||||||
|
return page.writeJitHTML([]byte("<div>"+page.Route+"<div>"), option.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (page *Page) publicFile(data map[string]interface{}) string {
|
func (page *Page) publicFile(data map[string]interface{}) string {
|
||||||
|
|
@ -354,3 +414,20 @@ func (page *Page) writeHTML(html []byte, data map[string]interface{}) error {
|
||||||
log.Trace("The page %s is removed", htmlFile)
|
log.Trace("The page %s is removed", htmlFile)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeHTMLTo write the html to file
|
||||||
|
func (page *Page) writeJitHTML(html []byte, data map[string]interface{}) error {
|
||||||
|
htmlFile := fmt.Sprintf("%s.jit", page.publicFile(data))
|
||||||
|
htmlFileAbs := filepath.Join(application.App.Root(), htmlFile)
|
||||||
|
dir := filepath.Dir(htmlFileAbs)
|
||||||
|
if exist, _ := os.Stat(dir); exist == nil {
|
||||||
|
os.MkdirAll(dir, os.ModePerm)
|
||||||
|
}
|
||||||
|
err := os.WriteFile(htmlFileAbs, html, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
core.RemoveCache(htmlFile)
|
||||||
|
log.Trace("The page %s is removed", htmlFile)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,49 @@ func (tmpl *Template) GetRoot() string {
|
||||||
return tmpl.Root
|
return tmpl.Root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Glob the files
|
||||||
|
func (tmpl *Template) Glob(pattern string) ([]string, error) {
|
||||||
|
path := filepath.Join(tmpl.Root, pattern)
|
||||||
|
paths, err := tmpl.local.fs.Glob(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
routes := []string{}
|
||||||
|
for _, p := range paths {
|
||||||
|
routes = append(routes, strings.TrimPrefix(p, tmpl.Root))
|
||||||
|
}
|
||||||
|
return routes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobRoutes the files
|
||||||
|
func (tmpl *Template) GlobRoutes(patterns []string, unique ...bool) ([]string, error) {
|
||||||
|
routes := []string{}
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
paths, err := tmpl.Glob(pattern)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
routes = append(routes, paths...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unique
|
||||||
|
if len(unique) > 0 && unique[0] {
|
||||||
|
mapRoutes := map[string]bool{}
|
||||||
|
for _, route := range routes {
|
||||||
|
mapRoutes[route] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
routes = []string{}
|
||||||
|
for route := range mapRoutes {
|
||||||
|
routes = append(routes, route)
|
||||||
|
}
|
||||||
|
return routes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return routes, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Reload the template
|
// Reload the template
|
||||||
func (tmpl *Template) Reload() error {
|
func (tmpl *Template) Reload() error {
|
||||||
newTmpl, err := tmpl.local.getTemplateFrom(tmpl.Root)
|
newTmpl, err := tmpl.local.getTemplateFrom(tmpl.Root)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ type Template struct {
|
||||||
Root string `json:"-"`
|
Root string `json:"-"`
|
||||||
local *Local
|
local *Local
|
||||||
locales []core.SelectOption
|
locales []core.SelectOption
|
||||||
|
loaded map[string]core.IPage
|
||||||
*core.Template
|
*core.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue