Skip to content

Commit b5b7142

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 PR review updates
1 parent 8118314 commit b5b7142

File tree

12 files changed

+1005
-86
lines changed

12 files changed

+1005
-86
lines changed

README.md

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -272,18 +272,43 @@ More details at Confluence [Code Block Macro](https://confluence.atlassian.com/d
272272

273273
### Block Quotes
274274

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

277302
1. The BlockQuote is on the root level of the document (not nested)
278-
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]`
279-
280-
| Github Alerts | Confluence |
281-
| --- | --- |
282-
| Tip (green lightbulb) | Tip (green checkmark in circle) |
283-
| Note (blue I in circle) | Info (blue I in circle) |
284-
| Important (purple exclamation mark in speech bubble) | Info (blue I in circle) |
285-
| Warning (yellow exclamation mark in triangle) | Note (yellow exclamation mark in triangle) |
286-
| Caution (red exclamation mark in hexagon) | Warning (red exclamation mark in hexagon) |
303+
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]`
304+
305+
| Github Alerts | Confluence | Description |
306+
| --------------- | ------------ | ------------- |
307+
| `[!TIP]` (green lightbulb) | Tip (green checkmark in circle) | Helpful suggestions and best practices |
308+
| `[!NOTE]` (blue I in circle) | Info (blue I in circle) | General information and notes |
309+
| `[!IMPORTANT]` (purple exclamation mark in speech bubble) | Info (blue I in circle) | Critical information that needs attention |
310+
| `[!WARNING]` (yellow exclamation mark in triangle) | Note (yellow exclamation mark in triangle) | Important warnings and cautions |
311+
| `[!CAUTION]` (red exclamation mark in hexagon) | Warning (red exclamation mark in hexagon) | Dangerous situations requiring immediate attention |
287312

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

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),
@@ -89,10 +91,10 @@ func (c *ConfluenceExtension) Extend(m goldmark.Markdown) {
8991
))
9092
}
9193

92-
func CompileMarkdown(markdown []byte, stdlib *stdlib.Lib, path string, cfg types.MarkConfig) (string, []attachment.Attachment) {
93-
log.Tracef(nil, "rendering markdown:\n%s", string(markdown))
94-
95-
confluenceExtension := NewConfluenceExtension(stdlib, path, cfg)
94+
// compileMarkdownWithExtension is a shared helper to eliminate code duplication
95+
// between different compilation approaches
96+
func compileMarkdownWithExtension(markdown []byte, ext goldmark.Extender, logMessage string) (string, []attachment.Attachment) {
97+
log.Tracef(nil, logMessage, string(markdown))
9698

9799
converter := goldmark.New(
98100
goldmark.WithExtensions(
@@ -101,7 +103,7 @@ func CompileMarkdown(markdown []byte, stdlib *stdlib.Lib, path string, cfg types
101103
extension.NewTable(
102104
extension.WithTableCellAlignMethod(extension.TableCellAlignStyle),
103105
),
104-
confluenceExtension,
106+
ext,
105107
extension.GFM,
106108
),
107109
goldmark.WithParserOptions(
@@ -122,8 +124,148 @@ func CompileMarkdown(markdown []byte, stdlib *stdlib.Lib, path string, cfg types
122124
}
123125

124126
html := buf.Bytes()
125-
126127
log.Tracef(nil, "rendered markdown to html:\n%s", string(html))
127128

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

0 commit comments

Comments
 (0)