feat: add GitHub Alerts transformer and renderers

Co-Authored-By: Manuel Rüger <manuel@rueg.eu>
This commit is contained in:
Noam Asor
2026-03-30 14:04:18 +02:00
committed by Manuel Rüger
parent b1de69c46a
commit 1c1eeb84fb
12 changed files with 1005 additions and 88 deletions

View File

@@ -10,23 +10,15 @@ import (
"github.com/yuin/goldmark/util"
)
// ConfluenceTextRenderer slightly alters the default goldmark behavior for
// inline text block. It allows for soft breaks
// (c.f. https://spec.commonmark.org/0.30/#softbreak)
// to be rendered into HTML as either '\n' (the goldmark default)
// or as ' '.
// This latter option is useful for Confluence,
// which inserts <br> tags into uploaded HTML where it sees '\n'.
// See also https://sembr.org/ for partial motivation.
type ConfluenceTextRenderer struct {
html.Config
softBreak rune
}
// NewConfluenceTextRenderer creates a new instance of the ConfluenceTextRenderer
func NewConfluenceTextRenderer(stripNL bool, opts ...html.Option) renderer.NodeRenderer {
// NewConfluenceTextRenderer creates a new instance of the renderer with GitHub Alerts support
func NewConfluenceTextRenderer(stripNewlines bool, opts ...html.Option) renderer.NodeRenderer {
sb := '\n'
if stripNL {
if stripNewlines {
sb = ' '
}
return &ConfluenceTextRenderer{
@@ -35,18 +27,36 @@ func NewConfluenceTextRenderer(stripNL bool, opts ...html.Option) renderer.NodeR
}
}
// RegisterFuncs implements NodeRenderer.RegisterFuncs .
// RegisterFuncs implements NodeRenderer.RegisterFuncs
func (r *ConfluenceTextRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindText, r.renderText)
}
// This is taken from https://github.com/yuin/goldmark/blob/v1.6.0/renderer/html/html.go#L719
// with the hardcoded '\n' for soft breaks swapped for the configurable r.softBreak
// renderText handles text rendering and supports GitHub Alerts replacement content.
// This is an enhanced version of the default goldmark text renderer that checks
// for replacement-content attributes before falling back to standard behavior.
// Note: This logic is partially duplicated from ConfluenceTextLegacyRenderer.renderText
// but includes additional GitHub Alerts support. We keep them separate to maintain
// clean legacy vs enhanced implementation paths.
func (r *ConfluenceTextRenderer) renderText(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
n := node.(*ast.Text)
// Check if this text node has replacement content from the GHAlerts transformer
if replacementContent, hasAttribute := node.Attribute([]byte("replacement-content")); hasAttribute && replacementContent != nil {
if contentBytes, ok := replacementContent.([]byte); ok {
_, err := w.Write(contentBytes)
if err != nil {
return ast.WalkStop, err
}
return ast.WalkContinue, nil
}
}
// Default text rendering behavior (same as original ConfluenceTextRenderer)
segment := n.Segment
if n.IsRaw() {
r.Writer.RawWrite(w, segment.Value(source))
@@ -87,6 +97,7 @@ func (r *ConfluenceTextRenderer) renderText(w util.BufWriter, source []byte, nod
}
}
}
return ast.WalkContinue, nil
}