Compare commits
10 commits
527dcac884
...
5e3c6d42f7
Author | SHA1 | Date | |
---|---|---|---|
5e3c6d42f7 | |||
4aea4ae42c | |||
0b31c9f888 | |||
![]() |
809b89d689 | ||
![]() |
463c8b4a85 | ||
![]() |
1c7f9f2f7d | ||
![]() |
9e6767fb12 | ||
![]() |
891e3938fa | ||
![]() |
d7fd8c74e8 | ||
![]() |
3b776ef8cd |
6 changed files with 187 additions and 78 deletions
19
README.md
19
README.md
|
@ -4,20 +4,23 @@ A markdown renderer that supports only a subset of the CommonMark spec in order
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Code blocks
|
- Bold
|
||||||
- Formatting (bold, italic, monospace)
|
- Code
|
||||||
|
- Italic
|
||||||
- Links
|
- Links
|
||||||
- Lists
|
- Lists
|
||||||
|
- Images
|
||||||
- Headers
|
- Headers
|
||||||
- Paragraphs
|
- Paragraphs
|
||||||
- Quotes
|
- Quotes
|
||||||
- Separators
|
- Separators
|
||||||
|
- Strikethrough
|
||||||
- Tables
|
- Tables
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
go get git.akyoto.dev/go/markdown
|
go get git.urbach.dev/go/markdown
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
@ -34,7 +37,9 @@ PASS: TestParagraph
|
||||||
PASS: TestHeader
|
PASS: TestHeader
|
||||||
PASS: TestItalic
|
PASS: TestItalic
|
||||||
PASS: TestBold
|
PASS: TestBold
|
||||||
|
PASS: TestStrike
|
||||||
PASS: TestLink
|
PASS: TestLink
|
||||||
|
PASS: TestImage
|
||||||
PASS: TestList
|
PASS: TestList
|
||||||
PASS: TestTables
|
PASS: TestTables
|
||||||
PASS: TestCode
|
PASS: TestCode
|
||||||
|
@ -48,14 +53,14 @@ coverage: 100.0% of statements
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
|
||||||
```
|
```
|
||||||
BenchmarkSmall-12 5986922 187.5 ns/op 32 B/op 1 allocs/op
|
BenchmarkSmall-12 5884641 201.5 ns/op 32 B/op 1 allocs/op
|
||||||
BenchmarkMedium-12 1000000 1077 ns/op 512 B/op 1 allocs/op
|
BenchmarkMedium-12 938371 1124 ns/op 512 B/op 1 allocs/op
|
||||||
BenchmarkLarge-12 255178 4055 ns/op 2560 B/op 2 allocs/op
|
BenchmarkLarge-12 277065 4115 ns/op 2560 B/op 2 allocs/op
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Please see the [license documentation](https://akyoto.dev/license).
|
Please see the [license documentation](https://urbach.dev/license).
|
||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
|
|
||||||
|
|
147
Render.go
147
Render.go
|
@ -17,6 +17,7 @@ type renderer struct {
|
||||||
paragraphLevel int
|
paragraphLevel int
|
||||||
quoteLevel int
|
quoteLevel int
|
||||||
listLevel int
|
listLevel int
|
||||||
|
olistLevel int
|
||||||
tableLevel int
|
tableLevel int
|
||||||
codeLines int
|
codeLines int
|
||||||
tableHeaderWritten bool
|
tableHeaderWritten bool
|
||||||
|
@ -200,6 +201,26 @@ func (r *renderer) processLine(line string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pos := 0
|
||||||
|
|
||||||
|
for pos < len(line) && line[pos] >= '0' && line[pos] <= '9' {
|
||||||
|
pos++
|
||||||
|
|
||||||
|
if pos < len(line) && (line[pos] == '.' || line[pos] == ')') {
|
||||||
|
line = strings.TrimSpace(line[pos+1:])
|
||||||
|
|
||||||
|
if r.olistLevel == 0 {
|
||||||
|
r.WriteString("<ol>")
|
||||||
|
r.olistLevel++
|
||||||
|
}
|
||||||
|
|
||||||
|
r.WriteString("<li>")
|
||||||
|
r.writeText(line)
|
||||||
|
r.WriteString("</li>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if r.paragraphLevel == 0 {
|
if r.paragraphLevel == 0 {
|
||||||
r.WriteString("<p>")
|
r.WriteString("<p>")
|
||||||
r.paragraphLevel++
|
r.paragraphLevel++
|
||||||
|
@ -233,7 +254,12 @@ func (r *renderer) closeLists() {
|
||||||
r.WriteString("</ul>")
|
r.WriteString("</ul>")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for range r.olistLevel {
|
||||||
|
r.WriteString("</ol>")
|
||||||
|
}
|
||||||
|
|
||||||
r.listLevel = 0
|
r.listLevel = 0
|
||||||
|
r.olistLevel = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// closeTables closes open tables.
|
// closeTables closes open tables.
|
||||||
|
@ -253,15 +279,15 @@ func (r *renderer) writeText(markdown string) {
|
||||||
searchStart = 0
|
searchStart = 0
|
||||||
linkTextStart = -1
|
linkTextStart = -1
|
||||||
linkTextEnd = -1
|
linkTextEnd = -1
|
||||||
urlStart = -1
|
linkIsImage = false
|
||||||
codeStart = -1
|
|
||||||
emStart = -1
|
emStart = -1
|
||||||
strongStart = -1
|
strongStart = -1
|
||||||
parentheses = 0
|
strikeStart = -1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
begin:
|
||||||
for {
|
for {
|
||||||
i := strings.IndexAny(markdown[searchStart:], "[]()`*_")
|
i := strings.IndexAny(markdown[searchStart:], "[]()`*_~!")
|
||||||
|
|
||||||
if i == -1 {
|
if i == -1 {
|
||||||
r.WriteString(html.EscapeString(markdown[tokenStart:]))
|
r.WriteString(html.EscapeString(markdown[tokenStart:]))
|
||||||
|
@ -270,9 +296,8 @@ func (r *renderer) writeText(markdown string) {
|
||||||
|
|
||||||
i += searchStart
|
i += searchStart
|
||||||
searchStart = i + 1
|
searchStart = i + 1
|
||||||
c := markdown[i]
|
|
||||||
|
|
||||||
switch c {
|
switch markdown[i] {
|
||||||
case '[':
|
case '[':
|
||||||
r.WriteString(html.EscapeString(markdown[tokenStart:i]))
|
r.WriteString(html.EscapeString(markdown[tokenStart:i]))
|
||||||
tokenStart = i
|
tokenStart = i
|
||||||
|
@ -282,45 +307,71 @@ func (r *renderer) writeText(markdown string) {
|
||||||
linkTextEnd = i
|
linkTextEnd = i
|
||||||
|
|
||||||
case '(':
|
case '(':
|
||||||
if parentheses == 0 {
|
if linkTextStart == -1 || linkTextEnd == -1 {
|
||||||
urlStart = i
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
parentheses++
|
level := 1
|
||||||
|
|
||||||
case ')':
|
for {
|
||||||
parentheses--
|
pos := strings.IndexAny(markdown[searchStart:], "()")
|
||||||
|
|
||||||
if parentheses == 0 && linkTextStart >= 0 && linkTextEnd >= 0 && urlStart >= 0 {
|
if pos == -1 {
|
||||||
linkText := markdown[linkTextStart+1 : linkTextEnd]
|
goto begin
|
||||||
linkURL := markdown[urlStart+1 : i]
|
}
|
||||||
|
|
||||||
r.WriteString("<a href=\"")
|
switch markdown[searchStart+pos] {
|
||||||
r.WriteString(sanitizeURL(linkURL))
|
case '(':
|
||||||
r.WriteString("\">")
|
level++
|
||||||
r.WriteString(html.EscapeString(linkText))
|
case ')':
|
||||||
r.WriteString("</a>")
|
level--
|
||||||
|
|
||||||
linkTextStart = -1
|
if level == 0 {
|
||||||
linkTextEnd = -1
|
urlEnd := searchStart + pos
|
||||||
urlStart = -1
|
searchStart = urlEnd + 1
|
||||||
|
|
||||||
tokenStart = i + 1
|
linkText := markdown[linkTextStart+1 : linkTextEnd]
|
||||||
|
linkURL := markdown[i+1 : urlEnd]
|
||||||
|
|
||||||
|
if linkIsImage {
|
||||||
|
r.WriteString("<img src=\"")
|
||||||
|
r.WriteString(sanitizeURL(linkURL))
|
||||||
|
r.WriteString("\" alt=\"")
|
||||||
|
r.WriteString(html.EscapeString(linkText))
|
||||||
|
r.WriteString("\">")
|
||||||
|
} else {
|
||||||
|
r.WriteString("<a href=\"")
|
||||||
|
r.WriteString(sanitizeURL(linkURL))
|
||||||
|
r.WriteString("\">")
|
||||||
|
r.WriteString(html.EscapeString(linkText))
|
||||||
|
r.WriteString("</a>")
|
||||||
|
}
|
||||||
|
|
||||||
|
linkTextStart = -1
|
||||||
|
linkTextEnd = -1
|
||||||
|
tokenStart = urlEnd + 1
|
||||||
|
goto begin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
searchStart += pos + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
case '`':
|
case '`':
|
||||||
if codeStart != -1 {
|
end := strings.IndexByte(markdown[searchStart:], '`')
|
||||||
r.WriteString("<code>")
|
|
||||||
r.WriteString(html.EscapeString(markdown[codeStart:i]))
|
if end == -1 {
|
||||||
r.WriteString("</code>")
|
continue
|
||||||
codeStart = -1
|
|
||||||
tokenStart = i + 1
|
|
||||||
} else {
|
|
||||||
r.WriteString(html.EscapeString(markdown[tokenStart:i]))
|
|
||||||
tokenStart = i
|
|
||||||
codeStart = i + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.WriteString(html.EscapeString(markdown[tokenStart:i]))
|
||||||
|
r.WriteString("<code>")
|
||||||
|
r.WriteString(html.EscapeString(markdown[searchStart : searchStart+end]))
|
||||||
|
r.WriteString("</code>")
|
||||||
|
|
||||||
|
searchStart += end + 1
|
||||||
|
tokenStart = searchStart
|
||||||
|
|
||||||
case '*', '_':
|
case '*', '_':
|
||||||
if i == emStart {
|
if i == emStart {
|
||||||
strongStart = i + 1
|
strongStart = i + 1
|
||||||
|
@ -343,6 +394,34 @@ func (r *renderer) writeText(markdown string) {
|
||||||
tokenStart = i
|
tokenStart = i
|
||||||
emStart = i + 1
|
emStart = i + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case '~':
|
||||||
|
if i+1 >= len(markdown) || markdown[i+1] != '~' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strikeStart != -1 {
|
||||||
|
r.WriteString("<del>")
|
||||||
|
r.WriteString(html.EscapeString(markdown[strikeStart:i]))
|
||||||
|
r.WriteString("</del>")
|
||||||
|
strikeStart = -1
|
||||||
|
tokenStart = i + 2
|
||||||
|
} else {
|
||||||
|
r.WriteString(html.EscapeString(markdown[tokenStart:i]))
|
||||||
|
tokenStart = i
|
||||||
|
strikeStart = i + 2
|
||||||
|
}
|
||||||
|
|
||||||
|
case '!':
|
||||||
|
if i+1 >= len(markdown) || markdown[i+1] != '[' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
r.WriteString(html.EscapeString(markdown[tokenStart:i]))
|
||||||
|
tokenStart = i
|
||||||
|
linkTextStart = i + 1
|
||||||
|
searchStart++
|
||||||
|
linkIsImage = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,4 +459,4 @@ func nextPowerOf2(x uint32) uint32 {
|
||||||
x |= x >> 16
|
x |= x >> 16
|
||||||
x++
|
x++
|
||||||
return x
|
return x
|
||||||
}
|
}
|
|
@ -3,8 +3,8 @@ package markdown_test
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.akyoto.dev/go/assert"
|
"git.urbach.dev/go/assert"
|
||||||
"git.akyoto.dev/go/markdown"
|
"git.urbach.dev/go/markdown"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEmpty(t *testing.T) {
|
func TestEmpty(t *testing.T) {
|
||||||
|
@ -39,20 +39,44 @@ func TestBold(t *testing.T) {
|
||||||
assert.Equal(t, markdown.Render("__bold__"), "<p><strong>bold</strong></p>")
|
assert.Equal(t, markdown.Render("__bold__"), "<p><strong>bold</strong></p>")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStrike(t *testing.T) {
|
||||||
|
assert.Equal(t, markdown.Render("~normal text~"), "<p>~normal text~</p>")
|
||||||
|
assert.Equal(t, markdown.Render("~~deleted text~~"), "<p><del>deleted text</del></p>")
|
||||||
|
}
|
||||||
|
|
||||||
func TestLink(t *testing.T) {
|
func TestLink(t *testing.T) {
|
||||||
assert.Equal(t, markdown.Render("[text](https://example.com/)"), "<p><a href=\"https://example.com/\">text</a></p>")
|
assert.Equal(t, markdown.Render("[text](https://example.com/)"), `<p><a href="https://example.com/">text</a></p>`)
|
||||||
assert.Equal(t, markdown.Render("[text](https://example.com/"), "<p>[text](https://example.com/</p>")
|
assert.Equal(t, markdown.Render("[text](https://example.com/"), `<p>[text](https://example.com/</p>`)
|
||||||
assert.Equal(t, markdown.Render("[text]https://example.com/)"), "<p>[text]https://example.com/)</p>")
|
assert.Equal(t, markdown.Render("[text]https://example.com/)"), `<p>[text]https://example.com/)</p>`)
|
||||||
assert.Equal(t, markdown.Render("[text(https://example.com/)"), "<p>[text(https://example.com/)</p>")
|
assert.Equal(t, markdown.Render("[text(https://example.com/)"), `<p>[text(https://example.com/)</p>`)
|
||||||
assert.Equal(t, markdown.Render("text](https://example.com/)"), "<p>text](https://example.com/)</p>")
|
assert.Equal(t, markdown.Render("text](https://example.com/)"), `<p>text](https://example.com/)</p>`)
|
||||||
assert.Equal(t, markdown.Render("Prefix [text](https://example.com/) suffix."), "<p>Prefix <a href=\"https://example.com/\">text</a> suffix.</p>")
|
assert.Equal(t, markdown.Render("[text](https://example.com/_test_)"), `<p><a href="https://example.com/_test_">text</a></p>`)
|
||||||
|
assert.Equal(t, markdown.Render("Prefix [text](https://example.com/) suffix."), `<p>Prefix <a href="https://example.com/">text</a> suffix.</p>`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestImage(t *testing.T) {
|
||||||
|
assert.Equal(t, markdown.Render("!"), `<p>!</p>`)
|
||||||
|
assert.Equal(t, markdown.Render("!["), `<p>![</p>`)
|
||||||
|
assert.Equal(t, markdown.Render("![]"), `<p>![]</p>`)
|
||||||
|
assert.Equal(t, markdown.Render(", `<p>
|
||||||
|
assert.Equal(t, markdown.Render("![]()"), `<p><img src="" alt=""></p>`)
|
||||||
|
assert.Equal(t, markdown.Render(""), `<p><img src="https://example.com/image.png" alt="title"></p>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestList(t *testing.T) {
|
func TestList(t *testing.T) {
|
||||||
|
assert.Equal(t, markdown.Render("-"), "<p>-</p>")
|
||||||
|
assert.Equal(t, markdown.Render("- "), "<ul><li></li></ul>")
|
||||||
assert.Equal(t, markdown.Render("- Entry"), "<ul><li>Entry</li></ul>")
|
assert.Equal(t, markdown.Render("- Entry"), "<ul><li>Entry</li></ul>")
|
||||||
assert.Equal(t, markdown.Render("- Entry 1\n- Entry 2"), "<ul><li>Entry 1</li><li>Entry 2</li></ul>")
|
assert.Equal(t, markdown.Render("- Entry 1\n- Entry 2"), "<ul><li>Entry 1</li><li>Entry 2</li></ul>")
|
||||||
assert.Equal(t, markdown.Render("- Entry 1\n- Entry 2\n- Entry 3"), "<ul><li>Entry 1</li><li>Entry 2</li><li>Entry 3</li></ul>")
|
}
|
||||||
assert.Equal(t, markdown.Render("-"), "<p>-</p>")
|
|
||||||
|
func TestOrderedList(t *testing.T) {
|
||||||
|
assert.Equal(t, markdown.Render("1"), "<p>1</p>")
|
||||||
|
assert.Equal(t, markdown.Render("1."), "<ol><li></li></ol>")
|
||||||
|
assert.Equal(t, markdown.Render("1. "), "<ol><li></li></ol>")
|
||||||
|
assert.Equal(t, markdown.Render("1. Entry"), "<ol><li>Entry</li></ol>")
|
||||||
|
assert.Equal(t, markdown.Render("999) Entry"), "<ol><li>Entry</li></ol>")
|
||||||
|
assert.Equal(t, markdown.Render("1. Entry\n2. Entry"), "<ol><li>Entry</li><li>Entry</li></ol>")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTables(t *testing.T) {
|
func TestTables(t *testing.T) {
|
||||||
|
@ -64,6 +88,7 @@ func TestCode(t *testing.T) {
|
||||||
assert.Equal(t, markdown.Render("```\nText\n```"), "<pre><code>Text</code></pre>")
|
assert.Equal(t, markdown.Render("```\nText\n```"), "<pre><code>Text</code></pre>")
|
||||||
assert.Equal(t, markdown.Render("```go\ntype A struct {\n\t\n}\n```"), "<pre><code class=\"language-go\">type A struct {\n\t\n}</code></pre>")
|
assert.Equal(t, markdown.Render("```go\ntype A struct {\n\t\n}\n```"), "<pre><code class=\"language-go\">type A struct {\n\t\n}</code></pre>")
|
||||||
assert.Equal(t, markdown.Render("`monospace`"), "<p><code>monospace</code></p>")
|
assert.Equal(t, markdown.Render("`monospace`"), "<p><code>monospace</code></p>")
|
||||||
|
assert.Equal(t, markdown.Render("`unfinished"), "<p>`unfinished</p>")
|
||||||
assert.Equal(t, markdown.Render("Inline `monospace` text."), "<p>Inline <code>monospace</code> text.</p>")
|
assert.Equal(t, markdown.Render("Inline `monospace` text."), "<p>Inline <code>monospace</code> text.</p>")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,22 +106,22 @@ func TestSeparator(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCombined(t *testing.T) {
|
func TestCombined(t *testing.T) {
|
||||||
assert.Equal(t, markdown.Render("# Header\n\nLine 1."), "<h1>Header</h1><p>Line 1.</p>")
|
assert.Equal(t, markdown.Render("# Header\n\nLine 1."), `<h1>Header</h1><p>Line 1.</p>`)
|
||||||
assert.Equal(t, markdown.Render("# Header\nLine 1.\nLine 2.\nLine 3."), "<h1>Header</h1><p>Line 1. Line 2. Line 3.</p>")
|
assert.Equal(t, markdown.Render("# Header\nLine 1.\nLine 2.\nLine 3."), `<h1>Header</h1><p>Line 1. Line 2. Line 3.</p>`)
|
||||||
assert.Equal(t, markdown.Render("# Header 1\nLine 1.\n# Header 2\nLine 2."), "<h1>Header 1</h1><p>Line 1.</p><h1>Header 2</h1><p>Line 2.</p>")
|
assert.Equal(t, markdown.Render("# Header 1\nLine 1.\n# Header 2\nLine 2."), `<h1>Header 1</h1><p>Line 1.</p><h1>Header 2</h1><p>Line 2.</p>`)
|
||||||
assert.Equal(t, markdown.Render("# [Header Link](https://example.com/)"), "<h1><a href=\"https://example.com/\">Header Link</a></h1>")
|
assert.Equal(t, markdown.Render("# [Header Link](https://example.com/)"), `<h1><a href="https://example.com/">Header Link</a></h1>`)
|
||||||
assert.Equal(t, markdown.Render("# Title\n\n- Entry 1\n- Entry 2\n\nText."), "<h1>Title</h1><ul><li>Entry 1</li><li>Entry 2</li></ul><p>Text.</p>")
|
assert.Equal(t, markdown.Render("# Title\n\n- Entry 1\n- Entry 2\n\nText."), `<h1>Title</h1><ul><li>Entry 1</li><li>Entry 2</li></ul><p>Text.</p>`)
|
||||||
assert.Equal(t, markdown.Render("- Entry\n# Header"), "<ul><li>Entry</li></ul><h1>Header</h1>")
|
assert.Equal(t, markdown.Render("- Entry\n# Header"), `<ul><li>Entry</li></ul><h1>Header</h1>`)
|
||||||
assert.Equal(t, markdown.Render("> - Entry\n> # Header"), "<blockquote><ul><li>Entry</li></ul><h1>Header</h1></blockquote>")
|
assert.Equal(t, markdown.Render("> - Entry\n> # Header"), `<blockquote><ul><li>Entry</li></ul><h1>Header</h1></blockquote>`)
|
||||||
assert.Equal(t, markdown.Render("> **bold** and *italic* text."), "<blockquote><p><strong>bold</strong> and <em>italic</em> text.</p></blockquote>")
|
assert.Equal(t, markdown.Render("> **bold** and *italic* text."), `<blockquote><p><strong>bold</strong> and <em>italic</em> text.</p></blockquote>`)
|
||||||
assert.Equal(t, markdown.Render("> __bold__ and _italic_ text."), "<blockquote><p><strong>bold</strong> and <em>italic</em> text.</p></blockquote>")
|
assert.Equal(t, markdown.Render("> __bold__ and _italic_ text."), `<blockquote><p><strong>bold</strong> and <em>italic</em> text.</p></blockquote>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSecurity(t *testing.T) {
|
func TestSecurity(t *testing.T) {
|
||||||
assert.Equal(t, markdown.Render("[text](javascript:alert(\"xss\"))"), "<p><a href=\"\">text</a></p>")
|
assert.Equal(t, markdown.Render(`[text](javascript:alert("xss"))`), `<p><a href="">text</a></p>`)
|
||||||
assert.Equal(t, markdown.Render("[text](javAscRipt:alert(\"xss\"))"), "<p><a href=\"\">text</a></p>")
|
assert.Equal(t, markdown.Render(`[text](javAscRipt:alert("xss"))`), `<p><a href="">text</a></p>`)
|
||||||
assert.Equal(t, markdown.Render("[text]( javascript:alert(\"xss\"))"), "<p><a href=\"\">text</a></p>")
|
assert.Equal(t, markdown.Render(`[text]( javascript:alert("xss"))`), `<p><a href="">text</a></p>`)
|
||||||
assert.Equal(t, markdown.Render("[text]('javAscRipt:alert(\"xss\")')"), "<p><a href=\"'javAscRipt:alert("xss")'\">text</a></p>")
|
assert.Equal(t, markdown.Render(`[text]('javAscRipt:alert("xss")')`), `<p><a href="'javAscRipt:alert("xss")'">text</a></p>`)
|
||||||
assert.Equal(t, markdown.Render("[text](\"><script>alert(123)</script>)"), "<p><a href=\""><script>alert(123)</script>\">text</a></p>")
|
assert.Equal(t, markdown.Render(`[text]("><script>alert(123)</script>)`), `<p><a href=""><script>alert(123)</script>">text</a></p>`)
|
||||||
assert.Equal(t, markdown.Render("[<script>alert(123)</script>]()"), "<p><a href=\"\"><script>alert(123)</script></a></p>")
|
assert.Equal(t, markdown.Render(`[<script>alert(123)</script>]()`), `<p><a href=""><script>alert(123)</script></a></p>`)
|
||||||
}
|
}
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.akyoto.dev/go/assert"
|
"git.urbach.dev/go/assert"
|
||||||
"git.akyoto.dev/go/markdown"
|
"git.urbach.dev/go/markdown"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkSmall(b *testing.B) {
|
func BenchmarkSmall(b *testing.B) {
|
||||||
|
@ -13,7 +13,7 @@ func BenchmarkSmall(b *testing.B) {
|
||||||
assert.Nil(b, err)
|
assert.Nil(b, err)
|
||||||
input := string(small)
|
input := string(small)
|
||||||
|
|
||||||
for range b.N {
|
for b.Loop() {
|
||||||
markdown.Render(input)
|
markdown.Render(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ func BenchmarkMedium(b *testing.B) {
|
||||||
assert.Nil(b, err)
|
assert.Nil(b, err)
|
||||||
input := string(medium)
|
input := string(medium)
|
||||||
|
|
||||||
for range b.N {
|
for b.Loop() {
|
||||||
markdown.Render(input)
|
markdown.Render(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ func BenchmarkLarge(b *testing.B) {
|
||||||
assert.Nil(b, err)
|
assert.Nil(b, err)
|
||||||
input := string(large)
|
input := string(large)
|
||||||
|
|
||||||
for range b.N {
|
for b.Loop() {
|
||||||
markdown.Render(input)
|
markdown.Render(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
6
go.mod
6
go.mod
|
@ -1,5 +1,5 @@
|
||||||
module git.akyoto.dev/go/markdown
|
module git.urbach.dev/go/markdown
|
||||||
|
|
||||||
go 1.22.1
|
go 1.24
|
||||||
|
|
||||||
require git.akyoto.dev/go/assert v0.1.3
|
require git.urbach.dev/go/assert v0.0.0-20250606150337-559d3d3afcda
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -1,2 +1,2 @@
|
||||||
git.akyoto.dev/go/assert v0.1.3 h1:QwCUbmG4aZYsNk/OuRBz1zWVKmGlDUHhOnnDBfn8Qw8=
|
git.urbach.dev/go/assert v0.0.0-20250606150337-559d3d3afcda h1:VN6ZQwtwLOm2xTms+v8IIeeNjvs55qyEBNArv3dPq9g=
|
||||||
git.akyoto.dev/go/assert v0.1.3/go.mod h1:0GzMaM0eURuDwtGkJJkCsI7r2aUKr+5GmWNTFPgDocM=
|
git.urbach.dev/go/assert v0.0.0-20250606150337-559d3d3afcda/go.mod h1:PNI/NSBOqvoeU58/7eBsIR09Yoq2S/qtSRiTrctkiq0=
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue