Skip to content

Commit 0d39702

Browse files
committed
initial transformer work
To update transformer renderers - compatibility work with original approach integration - step 1 integration - step 2 integration - step 3 integration - step 4
1 parent 7d6a63c commit 0d39702

File tree

12 files changed

+1012
-94
lines changed

12 files changed

+1012
-94
lines changed

README.md

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -265,18 +265,43 @@ some long code block
265265

266266
### Block Quotes
267267

268-
Block Quotes are converted to Confluence Info/Warn/Note box when the following conditions are met
268+
Mark now supports **GitHub Alerts** 🎉 - a modern way to create highlighted information boxes using familiar GitHub syntax!
269+
270+
#### GitHub Alerts Support
271+
272+
You can now use GitHub-style alert syntax in your markdown, and Mark will automatically convert them to beautiful Confluence macros:
273+
274+
```markdown
275+
> [!NOTE]
276+
> This creates a blue info box - perfect for helpful information!
277+
278+
> [!TIP]
279+
> This creates a green tip box - great for best practices and suggestions!
280+
281+
> [!IMPORTANT]
282+
> This creates a blue info box - ideal for critical information!
283+
284+
> [!WARNING]
285+
> This creates a yellow warning box - use for important warnings!
286+
287+
> [!CAUTION]
288+
> This creates a red warning box - perfect for dangerous situations!
289+
```
290+
291+
#### Technical Details
292+
293+
Block Quotes are converted to Confluence Info/Warn/Note box when the following conditions are met:
269294

270295
1. The BlockQuote is on the root level of the document (not nested)
271-
1. The first line of the BlockQuote contains one of the following patterns `Info/Warn/Note` or [Github MD Alerts style](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) `[!NOTE]/[!TIP]/[!IMPORTANT]/[!WARNING]/[!CAUTION]`
272-
273-
| Github Alerts | Confluence |
274-
|---------------|------------|
275-
| Tip (green lightbulb) | Tip (green checkmark in circle) |
276-
| Note (blue I in circle) | Info (blue I in circle) |
277-
| Important (purple exclamation mark in speech bubble) | Info (blue I in circle) |
278-
| Warning (yellow exclamation mark in triangle) | Note (yellow exclamation mark in triangle) |
279-
| Caution (red exclamation mark in hexagon) | Warning (red exclamation mark in hexagon) |
296+
2. The first line of the BlockQuote contains one of the following patterns `Info/Warn/Note` or [Github MD Alerts style](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) `[!NOTE]/[!TIP]/[!IMPORTANT]/[!WARNING]/[!CAUTION]`
297+
298+
| Github Alerts | Confluence | Description |
299+
|---------------|------------|-------------|
300+
| `[!TIP]` (green lightbulb) | Tip (green checkmark in circle) | Helpful suggestions and best practices |
301+
| `[!NOTE]` (blue I in circle) | Info (blue I in circle) | General information and notes |
302+
| `[!IMPORTANT]` (purple exclamation mark in speech bubble) | Info (blue I in circle) | Critical information that needs attention |
303+
| `[!WARNING]` (yellow exclamation mark in triangle) | Note (yellow exclamation mark in triangle) | Important warnings and cautions |
304+
| `[!CAUTION]` (red exclamation mark in hexagon) | Warning (red exclamation mark in hexagon) | Dangerous situations requiring immediate attention |
280305

281306
In any other case the default behaviour will be resumed and html `<blockquote>` tag will be used
282307

markdown/markdown.go

Lines changed: 157 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
cparser "github.com/kovetskiy/mark/parser"
99
crenderer "github.com/kovetskiy/mark/renderer"
1010
"github.com/kovetskiy/mark/stdlib"
11+
ctransformer "github.com/kovetskiy/mark/transformer"
1112
"github.com/kovetskiy/mark/types"
1213
"github.com/reconquest/pkg/log"
1314
mkDocsParser "github.com/stefanfritsch/goldmark-admonitions"
@@ -20,18 +21,19 @@ import (
2021
"github.com/yuin/goldmark/util"
2122
)
2223

23-
// Renderer renders anchor [Node]s.
24-
type ConfluenceExtension struct {
24+
// ConfluenceLegacyExtension is the original goldmark extension without GitHub Alerts support
25+
// This extension is preserved for backward compatibility and testing purposes
26+
type ConfluenceLegacyExtension struct {
2527
html.Config
2628
Stdlib *stdlib.Lib
2729
Path string
2830
MarkConfig types.MarkConfig
2931
Attachments []attachment.Attachment
3032
}
3133

32-
// NewConfluenceRenderer creates a new instance of the ConfluenceRenderer
33-
func NewConfluenceExtension(stdlib *stdlib.Lib, path string, cfg types.MarkConfig) *ConfluenceExtension {
34-
return &ConfluenceExtension{
34+
// NewConfluenceLegacyExtension creates a new instance of the legacy ConfluenceRenderer
35+
func NewConfluenceLegacyExtension(stdlib *stdlib.Lib, path string, cfg types.MarkConfig) *ConfluenceLegacyExtension {
36+
return &ConfluenceLegacyExtension{
3537
Config: html.NewConfig(),
3638
Stdlib: stdlib,
3739
Path: path,
@@ -40,14 +42,14 @@ func NewConfluenceExtension(stdlib *stdlib.Lib, path string, cfg types.MarkConfi
4042
}
4143
}
4244

43-
func (c *ConfluenceExtension) Attach(a attachment.Attachment) {
45+
func (c *ConfluenceLegacyExtension) Attach(a attachment.Attachment) {
4446
c.Attachments = append(c.Attachments, a)
4547
}
4648

47-
func (c *ConfluenceExtension) Extend(m goldmark.Markdown) {
49+
func (c *ConfluenceLegacyExtension) Extend(m goldmark.Markdown) {
4850

4951
m.Renderer().AddOptions(renderer.WithNodeRenderers(
50-
util.Prioritized(crenderer.NewConfluenceTextRenderer(c.MarkConfig.StripNewlines), 100),
52+
util.Prioritized(crenderer.NewConfluenceTextLegacyRenderer(c.MarkConfig.StripNewlines), 100),
5153
util.Prioritized(crenderer.NewConfluenceBlockQuoteRenderer(), 100),
5254
util.Prioritized(crenderer.NewConfluenceCodeBlockRenderer(c.Stdlib, c.Path), 100),
5355
util.Prioritized(crenderer.NewConfluenceFencedCodeBlockRenderer(c.Stdlib, c, c.MarkConfig), 100),
@@ -77,10 +79,10 @@ func (c *ConfluenceExtension) Extend(m goldmark.Markdown) {
7779
))
7880
}
7981

80-
func CompileMarkdown(markdown []byte, stdlib *stdlib.Lib, path string, cfg types.MarkConfig) (string, []attachment.Attachment) {
81-
log.Tracef(nil, "rendering markdown:\n%s", string(markdown))
82-
83-
confluenceExtension := NewConfluenceExtension(stdlib, path, cfg)
82+
// compileMarkdownWithExtension is a shared helper to eliminate code duplication
83+
// between different compilation approaches
84+
func compileMarkdownWithExtension(markdown []byte, ext goldmark.Extender, logMessage string) (string, []attachment.Attachment) {
85+
log.Tracef(nil, logMessage, string(markdown))
8486

8587
converter := goldmark.New(
8688
goldmark.WithExtensions(
@@ -89,7 +91,7 @@ func CompileMarkdown(markdown []byte, stdlib *stdlib.Lib, path string, cfg types
8991
extension.NewTable(
9092
extension.WithTableCellAlignMethod(extension.TableCellAlignStyle),
9193
),
92-
confluenceExtension,
94+
ext,
9395
extension.GFM,
9496
),
9597
goldmark.WithParserOptions(
@@ -110,8 +112,148 @@ func CompileMarkdown(markdown []byte, stdlib *stdlib.Lib, path string, cfg types
110112
}
111113

112114
html := buf.Bytes()
113-
114115
log.Tracef(nil, "rendered markdown to html:\n%s", string(html))
115116

116-
return string(html), confluenceExtension.Attachments
117+
// We'll return attachments separately - caller handles this
118+
return string(html), []attachment.Attachment{}
119+
}
120+
121+
// CompileMarkdown compiles markdown to Confluence Storage Format with GitHub Alerts support
122+
// This is the main function that now uses the enhanced GitHub Alerts transformer by default
123+
// for superior processing of [!NOTE], [!TIP], [!WARNING], [!CAUTION], [!IMPORTANT] syntax
124+
func CompileMarkdown(markdown []byte, stdlib *stdlib.Lib, path string, cfg types.MarkConfig) (string, []attachment.Attachment) {
125+
// Use the enhanced GitHub Alerts extension for better processing
126+
ghAlertsExtension := NewConfluenceExtension(stdlib, path, cfg)
127+
html, _ := compileMarkdownWithExtension(markdown, ghAlertsExtension, "rendering markdown with GitHub Alerts support:\n%s")
128+
return html, ghAlertsExtension.Attachments
129+
}
130+
131+
// CompileMarkdownLegacy compiles markdown using the legacy approach without GitHub Alerts transformer
132+
// This function is preserved for backward compatibility and testing purposes
133+
func CompileMarkdownLegacy(markdown []byte, stdlib *stdlib.Lib, path string, cfg types.MarkConfig) (string, []attachment.Attachment) {
134+
confluenceExtension := NewConfluenceLegacyExtension(stdlib, path, cfg)
135+
html, _ := compileMarkdownWithExtension(markdown, confluenceExtension, "rendering markdown with legacy renderer:\n%s")
136+
return html, confluenceExtension.Attachments
137+
}
138+
139+
// ConfluenceExtension is a goldmark extension for GitHub Alerts with Transformer approach
140+
// This extension provides superior GitHub Alert processing by transforming [!NOTE], [!TIP], etc.
141+
// into proper Confluence macros while maintaining full compatibility with existing functionality.
142+
// This is now the primary/default extension.
143+
type ConfluenceExtension struct {
144+
html.Config
145+
Stdlib *stdlib.Lib
146+
Path string
147+
MarkConfig types.MarkConfig
148+
Attachments []attachment.Attachment
149+
}
150+
151+
// NewConfluenceExtension creates a new instance of the GitHub Alerts extension
152+
// This is the improved standalone version that doesn't depend on feature flags
153+
func NewConfluenceExtension(stdlib *stdlib.Lib, path string, cfg types.MarkConfig) *ConfluenceExtension {
154+
return &ConfluenceExtension{
155+
Config: html.NewConfig(),
156+
Stdlib: stdlib,
157+
Path: path,
158+
MarkConfig: cfg,
159+
Attachments: []attachment.Attachment{},
160+
}
161+
}
162+
163+
func (c *ConfluenceExtension) Attach(a attachment.Attachment) {
164+
c.Attachments = append(c.Attachments, a)
165+
}
166+
167+
// Extend extends the Goldmark processor with GitHub Alerts transformer and renderers
168+
// This method registers all necessary components for GitHub Alert processing:
169+
// 1. Core renderers for standard markdown elements
170+
// 2. GitHub Alerts specific renderers (blockquote and text) with higher priority
171+
// 3. GitHub Alerts AST transformer for preprocessing
172+
func (c *ConfluenceExtension) Extend(m goldmark.Markdown) {
173+
// Register core renderers (excluding blockquote and text which we'll replace)
174+
m.Renderer().AddOptions(renderer.WithNodeRenderers(
175+
util.Prioritized(crenderer.NewConfluenceCodeBlockRenderer(c.Stdlib, c.Path), 100),
176+
util.Prioritized(crenderer.NewConfluenceFencedCodeBlockRenderer(c.Stdlib, c, c.MarkConfig), 100),
177+
util.Prioritized(crenderer.NewConfluenceHTMLBlockRenderer(c.Stdlib), 100),
178+
util.Prioritized(crenderer.NewConfluenceHeadingRenderer(c.MarkConfig.DropFirstH1), 100),
179+
util.Prioritized(crenderer.NewConfluenceImageRenderer(c.Stdlib, c, c.Path), 100),
180+
util.Prioritized(crenderer.NewConfluenceParagraphRenderer(), 100),
181+
util.Prioritized(crenderer.NewConfluenceLinkRenderer(), 100),
182+
))
183+
184+
// Add GitHub Alerts specific renderers with higher priority to override defaults
185+
// These renderers handle both GitHub Alerts and legacy blockquote syntax
186+
m.Renderer().AddOptions(renderer.WithNodeRenderers(
187+
util.Prioritized(crenderer.NewConfluenceGHAlertsBlockQuoteRenderer(), 200),
188+
util.Prioritized(crenderer.NewConfluenceTextRenderer(c.MarkConfig.StripNewlines), 200),
189+
))
190+
191+
// Add the GitHub Alerts AST transformer that preprocesses [!TYPE] syntax
192+
m.Parser().AddOptions(parser.WithASTTransformers(
193+
util.Prioritized(ctransformer.NewGHAlertsTransformer(), 100),
194+
))
195+
196+
// Add mkdocsadmonitions support if requested
197+
if slices.Contains(c.MarkConfig.Features, "mkdocsadmonitions") {
198+
m.Parser().AddOptions(
199+
parser.WithBlockParsers(
200+
util.Prioritized(mkDocsParser.NewAdmonitionParser(), 100),
201+
),
202+
)
203+
204+
m.Renderer().AddOptions(renderer.WithNodeRenderers(
205+
util.Prioritized(crenderer.NewConfluenceMkDocsAdmonitionRenderer(), 100),
206+
))
207+
}
208+
209+
// Add confluence tag parser for <ac:*/> tags
210+
m.Parser().AddOptions(parser.WithInlineParsers(
211+
util.Prioritized(cparser.NewConfluenceTagParser(), 199),
212+
))
213+
}
214+
215+
// CompileMarkdownWithTransformer compiles markdown using the transformer approach for GitHub Alerts
216+
// This function provides enhanced GitHub Alert processing while maintaining full compatibility
217+
// with existing markdown functionality. It transforms [!NOTE], [!TIP], etc. into proper titles.
218+
// This is an alias for CompileMarkdown for backward compatibility.
219+
func CompileMarkdownWithTransformer(markdown []byte, stdlib *stdlib.Lib, path string, cfg types.MarkConfig) (string, []attachment.Attachment) {
220+
return CompileMarkdown(markdown, stdlib, path, cfg)
221+
}
222+
223+
// Approach 2: Decorator Pattern Implementation
224+
// CompileMarkdownDecorator wraps the compilation process with configurable GitHub Alerts support
225+
226+
// MarkdownCompiler interface defines the contract for markdown compilation
227+
type MarkdownCompiler interface {
228+
Compile(markdown []byte, stdlib *stdlib.Lib, path string, cfg types.MarkConfig) (string, []attachment.Attachment)
229+
}
230+
231+
// LegacyMarkdownCompiler implements the original compilation without GitHub Alerts transformer
232+
type LegacyMarkdownCompiler struct{}
233+
234+
func (c *LegacyMarkdownCompiler) Compile(markdown []byte, stdlib *stdlib.Lib, path string, cfg types.MarkConfig) (string, []attachment.Attachment) {
235+
return CompileMarkdownLegacy(markdown, stdlib, path, cfg)
236+
}
237+
238+
// GHAlertsMarkdownCompiler implements compilation with GitHub Alerts transformer
239+
type GHAlertsMarkdownCompiler struct{}
240+
241+
func (c *GHAlertsMarkdownCompiler) Compile(markdown []byte, stdlib *stdlib.Lib, path string, cfg types.MarkConfig) (string, []attachment.Attachment) {
242+
return CompileMarkdownWithTransformer(markdown, stdlib, path, cfg)
243+
}
244+
245+
// CompileMarkdownWithDecorator allows choosing between legacy and GitHub Alerts approaches
246+
// This provides a flexible way to switch implementations based on configuration or feature flags
247+
func CompileMarkdownWithDecorator(markdown []byte, stdlib *stdlib.Lib, path string, cfg types.MarkConfig, useGHAlerts bool) (string, []attachment.Attachment) {
248+
var compiler MarkdownCompiler
249+
250+
if useGHAlerts {
251+
compiler = &GHAlertsMarkdownCompiler{}
252+
log.Tracef(nil, "using GitHub Alerts transformer compiler")
253+
} else {
254+
compiler = &LegacyMarkdownCompiler{}
255+
log.Tracef(nil, "using legacy compiler")
256+
}
257+
258+
return compiler.Compile(markdown, stdlib, path, cfg)
117259
}

0 commit comments

Comments
 (0)