mirror of https://github.com/jlelse/GoBlog
New plugin types: UISummary and UIFooter
parent
144e7f4a41
commit
8c9d17006d
|
@ -27,13 +27,15 @@ You need to specify the path to the plugin (remember to mount the path to your G
|
|||
|
||||
## Types of plugins
|
||||
|
||||
- `SetApp` (Access more GoBlog functionalities like the database) - see https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#SetApp
|
||||
- `SetConfig` (Access the configuration provided for the plugin) - see https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#SetConfig
|
||||
- `SetApp` (Access more GoBlog functionalities like the database) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#SetApp]
|
||||
- `SetConfig` (Access the configuration provided for the plugin) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#SetConfig]
|
||||
|
||||
- `Exec` (Command that is executed in a Go routine when starting GoBlog) - see https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#Exec
|
||||
- `Middleware` (HTTP middleware to intercept or modify HTTP requests) - see https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#Middleware
|
||||
- `UI` (Modify rendered HTML) - see https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#UI
|
||||
- `UI2` (Modify rendered HTML using a goquery document which improves performance and avoids multiple HTML parsing and rendering when using multiple plugins) - see https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#UI2
|
||||
- `Exec` (Command that is executed in a Go routine when starting GoBlog) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#Exec]
|
||||
- `Middleware` (HTTP middleware to intercept or modify HTTP requests) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/]plugintypes#Middleware
|
||||
- `UI` (Modify rendered HTML) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#UI]
|
||||
- `UI2` (Modify rendered HTML using a goquery document which improves performance and avoids multiple HTML parsing and rendering when using multiple plugins) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#UI2]
|
||||
- `UISummary` (like UI2 for only the post summary on indexes) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#UISummary]
|
||||
- `UIFooter` (like UI2 for only the post summary on indexes) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#UIFooter]
|
||||
|
||||
More types will be added later. Any plugin can implement multiple types, see the demo plugin as example.
|
||||
|
||||
|
|
|
@ -39,18 +39,24 @@ type Post interface {
|
|||
GetPath() string
|
||||
// Get a string array map with all the post's parameters
|
||||
GetParameters() map[string][]string
|
||||
// Get a single parameter array (a parameter can have multiple values)
|
||||
GetParameter(parameter string) []string
|
||||
// Get the post section name
|
||||
GetSection() string
|
||||
// Get the published date string
|
||||
GetPublished() string
|
||||
// Get the updated date string
|
||||
GetUpdated() string
|
||||
// Get the post content (markdown)
|
||||
GetContent() string
|
||||
}
|
||||
|
||||
// RenderContext
|
||||
type RenderContext interface {
|
||||
// Get the path of the request
|
||||
GetPath() string
|
||||
// Get the URL
|
||||
GetURL() string
|
||||
// Get the blog name
|
||||
GetBlog() string
|
||||
}
|
||||
|
|
|
@ -43,3 +43,18 @@ type UI2 interface {
|
|||
// The document can be used to add or modify HTML.
|
||||
RenderWithDocument(renderContext RenderContext, doc *goquery.Document)
|
||||
}
|
||||
|
||||
// UISummary plugins get called when rendering the summary on indexes for a post.
|
||||
type UISummary interface {
|
||||
// The renderContext provides information such as the path of the request or the blog name.
|
||||
// The post contains information about the post for which to render the summary.
|
||||
// The document can be used to add or modify the default HTML.
|
||||
RenderSummaryForPost(renderContext RenderContext, post Post, doc *goquery.Document)
|
||||
}
|
||||
|
||||
// UIFooter plugins get called when rendering the footer on each HTML page.
|
||||
type UIFooter interface {
|
||||
// The renderContext provides information such as the path of the request or the blog name.
|
||||
// The document can be used to add or modify the default HTML.
|
||||
RenderFooter(renderContext RenderContext, doc *goquery.Document)
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ func init() {
|
|||
"SetConfig": reflect.ValueOf((*plugintypes.SetConfig)(nil)),
|
||||
"UI": reflect.ValueOf((*plugintypes.UI)(nil)),
|
||||
"UI2": reflect.ValueOf((*plugintypes.UI2)(nil)),
|
||||
"UIFooter": reflect.ValueOf((*plugintypes.UIFooter)(nil)),
|
||||
"UISummary": reflect.ValueOf((*plugintypes.UISummary)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
"_App": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_App)(nil)),
|
||||
|
@ -59,6 +61,8 @@ func init() {
|
|||
"_SetConfig": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_SetConfig)(nil)),
|
||||
"_UI": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_UI)(nil)),
|
||||
"_UI2": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_UI2)(nil)),
|
||||
"_UIFooter": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_UIFooter)(nil)),
|
||||
"_UISummary": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_UISummary)(nil)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,6 +153,8 @@ func (W _go_goblog_app_app_pkgs_plugintypes_Middleware) Prio() int {
|
|||
// _go_goblog_app_app_pkgs_plugintypes_Post is an interface wrapper for Post type
|
||||
type _go_goblog_app_app_pkgs_plugintypes_Post struct {
|
||||
IValue interface{}
|
||||
WGetContent func() string
|
||||
WGetParameter func(parameter string) []string
|
||||
WGetParameters func() map[string][]string
|
||||
WGetPath func() string
|
||||
WGetPublished func() string
|
||||
|
@ -156,6 +162,12 @@ type _go_goblog_app_app_pkgs_plugintypes_Post struct {
|
|||
WGetUpdated func() string
|
||||
}
|
||||
|
||||
func (W _go_goblog_app_app_pkgs_plugintypes_Post) GetContent() string {
|
||||
return W.WGetContent()
|
||||
}
|
||||
func (W _go_goblog_app_app_pkgs_plugintypes_Post) GetParameter(parameter string) []string {
|
||||
return W.WGetParameter(parameter)
|
||||
}
|
||||
func (W _go_goblog_app_app_pkgs_plugintypes_Post) GetParameters() map[string][]string {
|
||||
return W.WGetParameters()
|
||||
}
|
||||
|
@ -177,6 +189,7 @@ type _go_goblog_app_app_pkgs_plugintypes_RenderContext struct {
|
|||
IValue interface{}
|
||||
WGetBlog func() string
|
||||
WGetPath func() string
|
||||
WGetURL func() string
|
||||
}
|
||||
|
||||
func (W _go_goblog_app_app_pkgs_plugintypes_RenderContext) GetBlog() string {
|
||||
|
@ -185,6 +198,9 @@ func (W _go_goblog_app_app_pkgs_plugintypes_RenderContext) GetBlog() string {
|
|||
func (W _go_goblog_app_app_pkgs_plugintypes_RenderContext) GetPath() string {
|
||||
return W.WGetPath()
|
||||
}
|
||||
func (W _go_goblog_app_app_pkgs_plugintypes_RenderContext) GetURL() string {
|
||||
return W.WGetURL()
|
||||
}
|
||||
|
||||
// _go_goblog_app_app_pkgs_plugintypes_SetApp is an interface wrapper for SetApp type
|
||||
type _go_goblog_app_app_pkgs_plugintypes_SetApp struct {
|
||||
|
@ -225,3 +241,23 @@ type _go_goblog_app_app_pkgs_plugintypes_UI2 struct {
|
|||
func (W _go_goblog_app_app_pkgs_plugintypes_UI2) RenderWithDocument(renderContext plugintypes.RenderContext, doc *goquery.Document) {
|
||||
W.WRenderWithDocument(renderContext, doc)
|
||||
}
|
||||
|
||||
// _go_goblog_app_app_pkgs_plugintypes_UIFooter is an interface wrapper for UIFooter type
|
||||
type _go_goblog_app_app_pkgs_plugintypes_UIFooter struct {
|
||||
IValue interface{}
|
||||
WRenderFooter func(renderContext plugintypes.RenderContext, doc *goquery.Document)
|
||||
}
|
||||
|
||||
func (W _go_goblog_app_app_pkgs_plugintypes_UIFooter) RenderFooter(renderContext plugintypes.RenderContext, doc *goquery.Document) {
|
||||
W.WRenderFooter(renderContext, doc)
|
||||
}
|
||||
|
||||
// _go_goblog_app_app_pkgs_plugintypes_UISummary is an interface wrapper for UISummary type
|
||||
type _go_goblog_app_app_pkgs_plugintypes_UISummary struct {
|
||||
IValue interface{}
|
||||
WRenderSummaryForPost func(renderContext plugintypes.RenderContext, post plugintypes.Post, doc *goquery.Document)
|
||||
}
|
||||
|
||||
func (W _go_goblog_app_app_pkgs_plugintypes_UISummary) RenderSummaryForPost(renderContext plugintypes.RenderContext, post plugintypes.Post, doc *goquery.Document) {
|
||||
W.WRenderSummaryForPost(renderContext, post, doc)
|
||||
}
|
||||
|
|
12
plugins.go
12
plugins.go
|
@ -22,6 +22,8 @@ const (
|
|||
pluginUi2Type = "ui2"
|
||||
pluginExecType = "exec"
|
||||
pluginMiddlewareType = "middleware"
|
||||
pluginUiSummaryType = "uisummary"
|
||||
pluginUiFooterType = "uifooter"
|
||||
)
|
||||
|
||||
func (a *goBlog) initPlugins() error {
|
||||
|
@ -37,6 +39,8 @@ func (a *goBlog) initPlugins() error {
|
|||
pluginUi2Type: reflect.TypeOf((*plugintypes.UI2)(nil)).Elem(),
|
||||
pluginExecType: reflect.TypeOf((*plugintypes.Exec)(nil)).Elem(),
|
||||
pluginMiddlewareType: reflect.TypeOf((*plugintypes.Middleware)(nil)).Elem(),
|
||||
pluginUiSummaryType: reflect.TypeOf((*plugintypes.UISummary)(nil)).Elem(),
|
||||
pluginUiFooterType: reflect.TypeOf((*plugintypes.UIFooter)(nil)).Elem(),
|
||||
},
|
||||
yaegiwrappers.Symbols,
|
||||
subFS,
|
||||
|
@ -106,6 +110,10 @@ func (p *post) GetParameters() map[string][]string {
|
|||
return p.Parameters
|
||||
}
|
||||
|
||||
func (p *post) GetParameter(parameter string) []string {
|
||||
return p.Parameters[parameter]
|
||||
}
|
||||
|
||||
func (p *post) GetSection() string {
|
||||
return p.Section
|
||||
}
|
||||
|
@ -117,3 +125,7 @@ func (p *post) GetPublished() string {
|
|||
func (p *post) GetUpdated() string {
|
||||
return p.Updated
|
||||
}
|
||||
|
||||
func (p *post) GetContent() string {
|
||||
return p.Content
|
||||
}
|
||||
|
|
|
@ -22,9 +22,10 @@ func GetPlugin() (
|
|||
plugintypes.UI2,
|
||||
plugintypes.Exec,
|
||||
plugintypes.Middleware,
|
||||
plugintypes.UISummary,
|
||||
) {
|
||||
p := &plugin{}
|
||||
return p, p, p, p, p, p
|
||||
return p, p, p, p, p, p, p
|
||||
}
|
||||
|
||||
// SetApp
|
||||
|
@ -97,3 +98,8 @@ func (p *plugin) Handler(next http.Handler) http.Handler {
|
|||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// UISummary
|
||||
func (p *plugin) RenderSummaryForPost(rc plugintypes.RenderContext, post plugintypes.Post, doc *goquery.Document) {
|
||||
doc.Find(".h-entry").AppendHtml(fmt.Sprintf("<p>Summary for post %s on %s</p>", post.GetPath(), rc.GetURL()))
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"go.goblog.app/app/pkgs/plugintypes"
|
||||
)
|
||||
|
||||
func GetPlugin() (plugintypes.SetConfig, plugintypes.UI2) {
|
||||
func GetPlugin() (plugintypes.SetConfig, plugintypes.UIFooter) {
|
||||
p := &plugin{}
|
||||
return p, p
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ func (p *plugin) SetConfig(config map[string]any) {
|
|||
p.config = config
|
||||
}
|
||||
|
||||
func (p *plugin) RenderWithDocument(rc plugintypes.RenderContext, doc *goquery.Document) {
|
||||
func (p *plugin) RenderFooter(rc plugintypes.RenderContext, doc *goquery.Document) {
|
||||
blog := rc.GetBlog()
|
||||
if blog == "" {
|
||||
fmt.Println("webrings plugin: blog is empty!")
|
||||
|
|
49
render.go
49
render.go
|
@ -20,6 +20,8 @@ type renderData struct {
|
|||
WebmentionReceivingEnabled bool
|
||||
TorUsed bool
|
||||
EasterEgg bool
|
||||
// For plugins
|
||||
prc *pluginRenderContext
|
||||
// Not directly accessible
|
||||
app *goBlog
|
||||
req *http.Request
|
||||
|
@ -40,36 +42,26 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st
|
|||
w.Header().Set(contentType, contenttype.HTMLUTF8)
|
||||
// Write status code
|
||||
w.WriteHeader(statusCode)
|
||||
// Render
|
||||
// Render (with UI2 plugins)
|
||||
renderPipeReader, renderPipeWriter := io.Pipe()
|
||||
go func() {
|
||||
f(htmlbuilder.NewHtmlBuilder(renderPipeWriter), data)
|
||||
hb, finish := a.wrapForPlugins(
|
||||
renderPipeWriter,
|
||||
a.getPlugins(pluginUi2Type),
|
||||
func(plugin any, doc *goquery.Document) {
|
||||
plugin.(plugintypes.UI2).RenderWithDocument(data.prc, doc)
|
||||
},
|
||||
)
|
||||
f(hb, data)
|
||||
finish()
|
||||
_ = renderPipeWriter.Close()
|
||||
}()
|
||||
// Create render context for plugins
|
||||
rc := &pluginRenderContext{
|
||||
blog: data.BlogString,
|
||||
path: r.URL.Path,
|
||||
}
|
||||
// Run UI plugins
|
||||
// Run io based UI plugins
|
||||
pluginPipeReader, pluginPipeWriter := io.Pipe()
|
||||
go func() {
|
||||
a.chainUiPlugins(a.getPlugins(pluginUiType), rc, renderPipeReader, pluginPipeWriter)
|
||||
a.chainUiPlugins(a.getPlugins(pluginUiType), data.prc, renderPipeReader, pluginPipeWriter)
|
||||
_ = pluginPipeWriter.Close()
|
||||
}()
|
||||
// Run UI2 plugins
|
||||
ui2Plugins := a.getPlugins(pluginUi2Type)
|
||||
if len(ui2Plugins) > 0 {
|
||||
doc, _ := goquery.NewDocumentFromReader(pluginPipeReader)
|
||||
_ = pluginPipeReader.Close()
|
||||
for _, plugin := range ui2Plugins {
|
||||
plugin.(plugintypes.UI2).RenderWithDocument(rc, doc)
|
||||
}
|
||||
pluginPipeReader, pluginPipeWriter = io.Pipe()
|
||||
go func() {
|
||||
_ = pluginPipeWriter.CloseWithError(goquery.Render(pluginPipeWriter, doc.Selection))
|
||||
}()
|
||||
}
|
||||
// Return minified HTML
|
||||
_ = pluginPipeReader.CloseWithError(a.min.Get().Minify(contenttype.HTML, w, pluginPipeReader))
|
||||
}
|
||||
|
@ -125,6 +117,14 @@ func (a *goBlog) checkRenderData(r *http.Request, data *renderData) {
|
|||
if ee := a.cfg.EasterEgg; ee != nil && ee.Enabled {
|
||||
data.EasterEgg = true
|
||||
}
|
||||
// Plugins
|
||||
if data.prc == nil {
|
||||
data.prc = &pluginRenderContext{
|
||||
blog: data.BlogString,
|
||||
path: r.URL.Path,
|
||||
url: a.getFullAddress(r.URL.Path),
|
||||
}
|
||||
}
|
||||
// Data
|
||||
if data.Data == nil {
|
||||
data.Data = map[string]any{}
|
||||
|
@ -136,6 +136,7 @@ func (a *goBlog) checkRenderData(r *http.Request, data *renderData) {
|
|||
type pluginRenderContext struct {
|
||||
blog string
|
||||
path string
|
||||
url string
|
||||
}
|
||||
|
||||
func (d *pluginRenderContext) GetBlog() string {
|
||||
|
@ -145,3 +146,7 @@ func (d *pluginRenderContext) GetBlog() string {
|
|||
func (d *pluginRenderContext) GetPath() string {
|
||||
return d.path
|
||||
}
|
||||
|
||||
func (d *pluginRenderContext) GetURL() string {
|
||||
return d.url
|
||||
}
|
||||
|
|
31
ui.go
31
ui.go
|
@ -140,34 +140,7 @@ func (a *goBlog) renderBase(hb *htmlbuilder.HtmlBuilder, rd *renderData, title,
|
|||
main(hb)
|
||||
}
|
||||
// Footer
|
||||
hb.WriteElementOpen("footer")
|
||||
// Footer menu
|
||||
if fm, ok := rd.Blog.Menus["footer"]; ok {
|
||||
hb.WriteElementOpen("nav")
|
||||
for i, item := range fm.Items {
|
||||
if i > 0 {
|
||||
hb.WriteUnescaped(" • ")
|
||||
}
|
||||
hb.WriteElementOpen("a", "href", item.Link)
|
||||
hb.WriteEscaped(a.renderMdTitle(item.Title))
|
||||
hb.WriteElementClose("a")
|
||||
}
|
||||
hb.WriteElementClose("nav")
|
||||
}
|
||||
// Copyright
|
||||
hb.WriteElementOpen("p", "translate", "no")
|
||||
hb.WriteUnescaped("© ")
|
||||
hb.WriteEscaped(time.Now().Format("2006"))
|
||||
hb.WriteUnescaped(" ")
|
||||
if user != nil && user.Name != "" {
|
||||
hb.WriteEscaped(user.Name)
|
||||
} else {
|
||||
hb.WriteEscaped(renderedBlogTitle)
|
||||
}
|
||||
hb.WriteElementClose("p")
|
||||
// Tor
|
||||
a.renderTorNotice(hb, rd)
|
||||
hb.WriteElementClose("footer")
|
||||
a.renderFooter(hb, rd)
|
||||
// Easter egg
|
||||
if rd.EasterEgg {
|
||||
hb.WriteElementOpen("script", "src", a.assetFileName("js/easteregg.js"), "defer", "")
|
||||
|
@ -409,7 +382,7 @@ func (a *goBlog) renderIndex(hb *htmlbuilder.HtmlBuilder, rd *renderData) {
|
|||
if id.posts != nil && len(id.posts) > 0 {
|
||||
// Posts
|
||||
for _, p := range id.posts {
|
||||
a.renderSummary(hb, rd.Blog, p, id.summaryTemplate)
|
||||
a.renderSummary(hb, rd, rd.Blog, p, id.summaryTemplate)
|
||||
}
|
||||
} else {
|
||||
// No posts
|
||||
|
|
|
@ -5,8 +5,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/samber/lo"
|
||||
"go.goblog.app/app/pkgs/htmlbuilder"
|
||||
"go.goblog.app/app/pkgs/plugintypes"
|
||||
)
|
||||
|
||||
type summaryTyp string
|
||||
|
@ -17,13 +19,18 @@ const (
|
|||
)
|
||||
|
||||
// post summary on index pages
|
||||
func (a *goBlog) renderSummary(hb *htmlbuilder.HtmlBuilder, bc *configBlog, p *post, typ summaryTyp) {
|
||||
func (a *goBlog) renderSummary(origHb *htmlbuilder.HtmlBuilder, rd *renderData, bc *configBlog, p *post, typ summaryTyp) {
|
||||
if bc == nil || p == nil {
|
||||
return
|
||||
}
|
||||
if typ == "" {
|
||||
typ = defaultSummary
|
||||
}
|
||||
// Plugin handling
|
||||
hb, finish := a.wrapForPlugins(origHb, a.getPlugins(pluginUiSummaryType), func(plugin any, doc *goquery.Document) {
|
||||
plugin.(plugintypes.UISummary).RenderSummaryForPost(rd.prc, p, doc)
|
||||
})
|
||||
defer finish()
|
||||
// Start article
|
||||
hb.WriteElementOpen("article", "class", "h-entry border-bottom")
|
||||
if p.Priority > 0 {
|
||||
|
@ -674,3 +681,40 @@ func (a *goBlog) renderUserSettings(hb *htmlbuilder.HtmlBuilder, rd *renderData,
|
|||
)
|
||||
hb.WriteElementClose("form")
|
||||
}
|
||||
|
||||
func (a *goBlog) renderFooter(origHb *htmlbuilder.HtmlBuilder, rd *renderData) {
|
||||
// Wrap plugins
|
||||
hb, finish := a.wrapForPlugins(origHb, a.getPlugins(pluginUiFooterType), func(plugin any, doc *goquery.Document) {
|
||||
plugin.(plugintypes.UIFooter).RenderFooter(rd.prc, doc)
|
||||
})
|
||||
defer finish()
|
||||
// Render footer
|
||||
hb.WriteElementOpen("footer")
|
||||
// Footer menu
|
||||
if fm, ok := rd.Blog.Menus["footer"]; ok {
|
||||
hb.WriteElementOpen("nav")
|
||||
for i, item := range fm.Items {
|
||||
if i > 0 {
|
||||
hb.WriteUnescaped(" • ")
|
||||
}
|
||||
hb.WriteElementOpen("a", "href", item.Link)
|
||||
hb.WriteEscaped(a.renderMdTitle(item.Title))
|
||||
hb.WriteElementClose("a")
|
||||
}
|
||||
hb.WriteElementClose("nav")
|
||||
}
|
||||
// Copyright
|
||||
hb.WriteElementOpen("p", "translate", "no")
|
||||
hb.WriteUnescaped("© ")
|
||||
hb.WriteEscaped(time.Now().Format("2006"))
|
||||
hb.WriteUnescaped(" ")
|
||||
if user := a.cfg.User; user != nil && user.Name != "" {
|
||||
hb.WriteEscaped(user.Name)
|
||||
} else {
|
||||
hb.WriteEscaped(a.renderMdTitle(rd.Blog.Title))
|
||||
}
|
||||
hb.WriteElementClose("p")
|
||||
// Tor
|
||||
a.renderTorNotice(hb, rd)
|
||||
hb.WriteElementClose("footer")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"go.goblog.app/app/pkgs/htmlbuilder"
|
||||
)
|
||||
|
||||
func (*goBlog) wrapForPlugins(
|
||||
originalWriter io.Writer,
|
||||
plugins []any,
|
||||
pluginRender func(plugin any, doc *goquery.Document),
|
||||
) (wrappedHb *htmlbuilder.HtmlBuilder, finish func()) {
|
||||
if len(plugins) == 0 {
|
||||
// No plugins, nothing to wrap
|
||||
if hb, ok := (originalWriter).(*htmlbuilder.HtmlBuilder); ok {
|
||||
return hb, func() {}
|
||||
}
|
||||
return htmlbuilder.NewHtmlBuilder(originalWriter), func() {}
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
pr, pw := io.Pipe()
|
||||
finish = func() {
|
||||
_ = pw.Close()
|
||||
wg.Wait()
|
||||
}
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
defer wg.Done()
|
||||
doc, err := goquery.NewDocumentFromReader(pr)
|
||||
_ = pr.CloseWithError(err)
|
||||
for _, plugin := range plugins {
|
||||
pluginRender(plugin, doc)
|
||||
}
|
||||
_ = goquery.Render(originalWriter, doc.Selection)
|
||||
}()
|
||||
return htmlbuilder.NewHtmlBuilder(pw), finish
|
||||
}
|
Loading…
Reference in New Issue