go-md-jira
A Go library and command-line tool for converting between Markdown and Jira wiki markup. Inspired by https://github.com/eshack94/md-to-jira
Features
- Fast Performance: Pre-compiled regex patterns for optimal conversion speed
- Comprehensive Conversion: Headers, lists, text formatting, code blocks, and links
- Bidirectional Conversion: Markdown -> Jira wiki and Jira wiki -> Markdown
- Smart Protection: Placeholder-based system prevents formatting conflicts
- Nested Lists: Proper handling of multi-level ordered and unordered lists
- Code Block Support: Fenced code blocks with optional language specification
- CLI and Library: Use as standalone tool or import into your Go projects
Quick Start
Command Line Usage
# Clone the repository
git clone https://github.com/dja852/go-md-jira.git
cd go-md-jira
# Convert a markdown file to Jira wiki (default direction)
go run ./cmd input.md
# Convert Jira wiki to Markdown
go run ./cmd -d jira-to-md input.jira
# Or build and run
go build -o md2jira ./cmd
./md2jira input.md
./md2jira -d jira-to-md input.jira
Library Usage
package main
import (
"fmt"
"log"
gomdjira "github.com/dja852/go-md-jira"
)
func main() {
// Convert markdown string directly
markdown := `# Header
This is **bold** and _italic_ text with {{inline code}}.
- List item 1
- List item 2`
jira := gomdjira.ConvertMarkdownString(markdown)
fmt.Println(jira)
// Convert markdown file and get result as string
result, err := gomdjira.ConvertMarkdownFile("input.md")
if err != nil {
log.Fatal(err)
}
fmt.Println(result)
// Convert markdown file and print to stdout
if err := gomdjira.MarkdownToJira("input.md"); err != nil {
log.Fatal(err)
}
// Convert Jira wiki string directly
jiraInput := "h1. Header\n\n# Ordered item"
md := gomdjira.ConvertJiraString(jiraInput)
fmt.Println(md)
// Convert Jira wiki file and print Markdown to stdout
if err := gomdjira.JiraToMarkdown("input.jira"); err != nil {
log.Fatal(err)
}
}
Installation as Dependency
go get github.com/dja852/go-md-jira
Conversion Examples
| Markdown |
Jira Markup |
Description |
# Header 1 |
h1. Header 1 |
Headers (h1-h6) |
**bold** |
*bold* |
Bold text |
_italic_ |
_italic_ |
Italic text |
`code` |
{{code}} |
Inline code |
~~strike~~ |
-strike- |
Strikethrough |
[link](url) |
[link|url] |
Links |
1. Item |
# Item |
Ordered lists |
- Item |
- Item |
Unordered lists |
- Nested |
-- Nested |
Nested lists (4-space indentation) |
Complex Example
Markdown Input:
# Project Documentation
This project uses **Go** and supports _multiple formats_.
## Features
1. Fast conversion with `pre-compiled` regex
2. Support for nested lists:
- Feature A
- Feature B
1. Sub-feature 1
2. Sub-feature 2
```go
func main() {
fmt.Println("Hello, World!")
}
```
Visit [GitHub](https://github.com/example/repo) for more info.
Jira Output:
h1. Project Documentation
This project uses *Go* and supports _multiple formats_.
h2. Features
# Fast conversion with {{pre-compiled}} regex
# Support for nested lists:
-- Feature A
-- Feature B
### Sub-feature 1
### Sub-feature 2
{code:go}
func main() {
fmt.Println("Hello, World!")
}
{code}
Visit [GitHub|https://github.com/example/repo] for more info.
API Reference
Core Functions
ConvertMarkdownString(markdown string) string
Converts a markdown string directly to Jira markup.
Parameters:
markdown: Input markdown text
Returns:
- Converted Jira markup string
ConvertMarkdownFile(filePath string) (string, error)
Reads a markdown file and returns the converted Jira markup as a string.
Parameters:
filePath: Path to the markdown file
Returns:
- Converted Jira markup string and error if file operations fail
MarkdownToJira(filePath string) error
Reads a markdown file and prints the converted Jira markup to stdout.
Parameters:
filePath: Path to the markdown file
Returns:
- Error if file operations fail, nil on success
Converts a markdown file and writes output to a custom writer.
Parameters:
inputPath: Path to the markdown file
writer: io.Writer to receive the output
Returns:
- Error if file operations fail, nil on success
ConvertJiraString(jira string) string
Converts a Jira wiki string directly to Markdown.
ConvertJiraFile(filePath string) (string, error)
Reads a Jira wiki file and returns converted Markdown as a string.
JiraToMarkdown(filePath string) error
Reads a Jira wiki file and prints converted Markdown to stdout.
Converts a Jira wiki file and writes Markdown to a custom writer.
Regular Expression Patterns
The converter uses pre-compiled regex patterns for optimal performance:
// Matches markdown headers and converts to Jira format
^######\s*(.+) → h6. ${1} // # Header 6
^#####\s*(.+) → h5. ${1} // # Header 5
^####\s*(.+) → h4. ${1} // # Header 4
^###\s*(.+) → h3. ${1} // # Header 3
^##\s*(.+) → h2. ${1} // # Header 2
^#\s*(.+) → h1. ${1} // # Header 1
Text Formatting Patterns
\*\*(.+?)\*\* // **bold** → *bold*
__(.+?)__ // __bold__ → *bold*
\*([^*]+)\* // *italic* → _italic_
_(.+?)_ // _italic_ → _italic_
`([^`]+)` // `code` → {{code}}
~~(.+?)~~ // ~~strike~~ → -strike-
Link Pattern
\[(.*?)\]\((.+?)\) // [text](url) → [text|url]
List Patterns
^\d+\.\s+ // "1. Item" → "# Item"
^\*\s+ // "* Item" → "- Item"
^( {2,})\d+\.\s+ // Nested ordered lists
^( {2,})[*-]\s+ // Nested unordered lists
Code Block Pattern
(?s)```(\w+)?\n(.*?)\n``` // Fenced code blocks with optional language
Conversion Logic
Processing Flow
-
Placeholder Protection: Inline code and bold text are temporarily replaced with placeholders (§INLINECODE§, §BOLD§) to prevent interference with other patterns
-
Header Processing: Headers are processed first as they take precedence over other formatting
-
List Processing:
- Nested lists are calculated based on 4-space indentation levels
- Ordered lists use
#, ##, ### for nesting
- Unordered lists use
-, --, --- for nesting
-
Text Formatting: Bold, italic, strikethrough, and links are processed
-
Code Block Processing: Fenced code blocks are converted to Jira {code:lang} format
-
Placeholder Restoration: Original content is restored from placeholders
Nested List Level Calculation
The converter uses 4-space indentation to determine nesting levels:
level := (len(spaces) / 4) + 1 // 4 spaces = 1 level deeper
Examples:
- Item (4 spaces) → -- Item (level 2)
1. Item (8 spaces) → ### Item (level 3)
- Item (12 spaces) → ---- Item (level 4)
- Pre-compiled Regex: All patterns are compiled once at package initialization
- Placeholder System: Prevents multiple passes over the same content
- Single-pass Processing: Most conversions happen in a single iteration
- Efficient String Operations: Uses
strings.Builder for concatenation
Testing
Run the test suite:
go test -v
Run performance benchmarks:
go test -bench=.
Current benchmark results:
BenchmarkConvertMarkdownString-8 50000 ~30000 ns/op
Architecture
Package Structure
go-md-jira/
├── go-md-jira.go # Core conversion logic
├── benchmark_test.go # Tests and benchmarks
├── cmd/
│ └── converter.go # Command-line interface
├── go.mod # Go module definition
└── README.md # This documentation
Design Decisions
- No Indented Code Blocks: Disabled to avoid conflicts with nested lists (architectural choice for clarity)
- Placeholder Protection: Prevents nested formatting issues with complex markdown
- Pre-compiled Regex: Performance optimization for repeated conversions
- Single Package: Simple, focused API surface
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
License
See LICENSE file for details.
- Pre-compiled regex patterns provide ~10x performance improvement over on-demand compilation
- Placeholder protection system adds minimal overhead while preventing conversion errors
- Memory efficient with string builders and single-pass processing
- Suitable for high-throughput conversion scenarios