Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions benchmarks/plugin-emoji.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { bench, run, group, barplot } from 'mitata'
import MarkdownIt from 'markdown-it'
import MarkdownExit from 'markdown-exit'
import { markdownItComark } from 'comark/plugins/syntax'
import { full as markdownItEmoji } from 'markdown-it-emoji'
import { createParse } from 'comark'
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkGfm from 'remark-gfm'
import remarkEmoji from 'remark-emoji'
import emoji from '../packages/comark/src/plugins/emoji'

const short = `Hello :wave: world :rocket: that's :100: percent :fire:`

const medium = `
# Welcome :tada:

This is a **great** day :sunny: for coding :computer:

## Features :sparkles:

- Fast parsing :rocket:
- Easy to use :thumbsup:
- Great docs :books:

> "Keep it simple" :smile: -- someone wise :nerd_face:

Check out our :star: project on :octocat: GitHub!

Don't :x: forget to :heart: the repo :pray:
`

const long = Array.from(
{ length: 50 },
(_, i) => `
## Section ${i + 1} :bookmark:

Paragraph ${i + 1} has some :smile: emoji and :rocket: inline codes.

- Item :thumbsup: with emoji
- Another :star: item
- Final :checkered_flag: item

> Quote with :heart: and :fire: emoji :100:
`
).join('\n')

// markdown-it: baseline vs emoji
const markdownIt = new MarkdownIt({ html: false, linkify: true })
.enable(['table', 'strikethrough'])
.use(markdownItComark)
const markdownItEm = new MarkdownIt({ html: false, linkify: true })
.enable(['table', 'strikethrough'])
.use(markdownItComark)
.use(markdownItEmoji)

// markdown-exit: baseline vs emoji
const markdownExit = new MarkdownExit({ html: false, linkify: true })
.enable(['table', 'strikethrough'])
.use(markdownItComark)
const markdownExitEm = new MarkdownExit({ html: false, linkify: true })
.enable(['table', 'strikethrough'])
.use(markdownItComark)
.use(markdownItEmoji)

// comark: baseline vs emoji plugin
const comark = createParse()
const comarkEm = createParse({ plugins: [emoji()] })

// remark (unified): baseline vs emoji
const remark = unified().use(remarkParse).use(remarkGfm)
const remarkEm = unified().use(remarkParse).use(remarkGfm).use(remarkEmoji)

for (const [label, content] of [
['short text', short],
['medium text', medium],
['long text (50 sections)', long],
] as const) {
barplot(() => {
group(`emoji β€” ${label}`, () => {
bench('comark', async () => {
await comark(content)
})
bench('comark + emoji', async () => {
await comarkEm(content)
})
bench('markdown-it', () => {
markdownIt.parse(content, {})
})
bench('markdown-it + emoji', () => {
markdownItEm.parse(content, {})
})
bench('markdown-exit', () => {
markdownExit.parse(content, {})
})
bench('markdown-exit + emoji', () => {
markdownExitEm.parse(content, {})
})
bench('remark', () => {
remark.parse(content)
})
bench('remark + emoji', () => {
remarkEm.runSync(remarkEm.parse(content))
})
})
})
}

console.log('πŸƒ Running benchmarks...\n')
await run()
158 changes: 57 additions & 101 deletions benchmarks/plugin-punctuation.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { bench, run, group, barplot } from 'mitata'
import MarkdownIt from 'markdown-it'
import MarkdownExit from 'markdown-exit'
import { markdownItComark } from 'comark/plugins/syntax'
import { createParse } from 'comark'
import { log } from '@comark/ansi'
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkGfm from 'remark-gfm'
import remarkSmartypants from 'remark-smartypants'
import punctuation from '../packages/comark/src/plugins/punctuation'

// ── Test content (exercises ALL features: quotes, dashes, ellipsis, symbols, normalization) ──
// Test content (exercises ALL features: quotes, dashes, ellipsis, symbols, normalization)

const short = `"Hello" -- world... (c) 2025 what???? ok,,`

Expand Down Expand Up @@ -38,113 +42,65 @@ Really???? Wow!!!!! hmm,, ok?.... end
`
).join('\n')

// ── markdown-it typographer ─────────────────────────────────────────────────

const parserTypographer = new MarkdownExit({
html: false,
linkify: true,
typographer: true,
})
// markdown-it: baseline vs typographer
const markdownIt = new MarkdownIt({ html: false, linkify: true, typographer: false })
.enable(['table', 'strikethrough'])
.use(markdownItComark)

const parserNoTypographer = new MarkdownExit({
html: false,
linkify: true,
typographer: false,
})
const markdownItTypo = new MarkdownIt({ html: false, linkify: true, typographer: true })
.enable(['table', 'strikethrough'])
.use(markdownItComark)

// ── comark with full punctuation plugin (all features) ──────────────────────

const comarkFull = createParse({ plugins: [punctuation()] })
const comarkBaseline = createParse()

// ── Benchmarks ──────────────────────────────────────────────────────────────

barplot(() => {
group('short text (all features)', () => {
bench('markdown-it typographer', () => {
parserTypographer.parse(short, {})
})
bench('markdown-it baseline', () => {
parserNoTypographer.parse(short, {})
})
bench('comark + punctuation (full)', async () => {
await comarkFull(short)
})
bench('comark baseline', async () => {
await comarkBaseline(short)
})
})
})

barplot(() => {
group('medium text (all features)', () => {
bench('markdown-it typographer', () => {
parserTypographer.parse(medium, {})
})
bench('markdown-it baseline', () => {
parserNoTypographer.parse(medium, {})
})
bench('comark + punctuation (full)', async () => {
await comarkFull(medium)
})
bench('comark baseline', async () => {
await comarkBaseline(medium)
})
})
})
// markdown-exit: baseline vs typographer
const markdownExit = new MarkdownExit({ html: false, linkify: true, typographer: false })
.enable(['table', 'strikethrough'])
.use(markdownItComark)
const markdownExitTypo = new MarkdownExit({ html: false, linkify: true, typographer: true })
.enable(['table', 'strikethrough'])
.use(markdownItComark)

barplot(() => {
group('long text β€” 50 sections (all features)', () => {
bench('markdown-it typographer', () => {
parserTypographer.parse(long, {})
})
bench('markdown-it baseline', () => {
parserNoTypographer.parse(long, {})
})
bench('comark + punctuation (full)', async () => {
await comarkFull(long)
})
bench('comark baseline', async () => {
await comarkBaseline(long)
// comark: baseline vs punctuation plugin
const comark = createParse()
const comarkPunct = createParse({ plugins: [punctuation()] })

// remark (unified): baseline vs smartypants
const remark = unified().use(remarkParse).use(remarkGfm)
const remarkSmarty = unified().use(remarkParse).use(remarkGfm).use(remarkSmartypants)


for (const [label, content] of [
['short text', short],
['medium text', medium],
['long text (50 sections)', long],
] as const) {
barplot(() => {
group(`punctuation β€” ${label}`, () => {
bench('comark', async () => {
await comark(content)
})
bench('comark + punctuation', async () => {
await comarkPunct(content)
})
bench('markdown-it', () => {
markdownIt.parse(content, {})
})
bench('markdown-it + typographer', () => {
markdownItTypo.parse(content, {})
})
bench('markdown-exit', () => {
markdownExit.parse(content, {})
})
bench('markdown-exit + typographer', () => {
markdownExitTypo.parse(content, {})
})
bench('remark', () => {
remark.parse(content)
})
bench('remark + smartypants', () => {
remarkSmarty.runSync(remarkSmarty.parse(content))
})
})
})
})

// ── Output comparison ───────────────────────────────────────────────────────

function flattenText(nodes: any[]): string {
let text = ''
for (const node of nodes) {
if (typeof node === 'string') text += node
else if (Array.isArray(node) && node.length > 2) text += flattenText(node.slice(2))
}
return text
}

const testStr = `"Hello" -- world... (c) what???? ok,, really!.... hmm.....`

console.log('=== Output Comparison ===\n')
console.log('Input:', JSON.stringify(testStr))

const miTokens = parserTypographer.parse(testStr, {})
const miText = miTokens
.filter((t: any) => t.type === 'inline')
.map((t: any) => t.content)
.join('')
console.log('markdown-it typographer:', JSON.stringify(miText))

const comarkTree = await comarkFull(testStr)
console.log('comark punctuation: ', JSON.stringify(flattenText(comarkTree.nodes)))

console.log('\nπŸƒ Running benchmarks...\n')
console.log('πŸƒ Running benchmarks...\n')
await run()

await log(`> [!NOTE]
> The goal of this benchmark is to compare the additional time each parser takes when
> using punctuation plugins.
>
> Official plugin adds ~100% the execution time, while comark adds ~25% execution time`)
Loading
Loading