Skip to content
Merged
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
65 changes: 26 additions & 39 deletions packages/comark/benchmark.ts → benchmarks/comark-parse.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { bench, run } from 'mitata'
import { bench, run, barplot, group } from 'mitata'
import MarkdownIt from 'markdown-it'
import MarkdownExit from 'markdown-exit'
import pluginMdc from '@comark/markdown-it'
import { createParse } from './src/index'
import { renderHTMLForTest } from './test/utils/render-html'
import { createParse } from 'comark'

// Sample markdown content to test with
const sampleMarkdown = `---
Expand Down Expand Up @@ -76,42 +75,30 @@ const comark = createParse()
const comarkNoClose = createParse({ autoClose: false })
const comarkStreaming = createParse()

// Benchmark: markdown-it parsing
bench('markdown-it parse', () => {
markdownIt.parse(sampleMarkdown, {})
})

// Benchmark: markdown-exit parsing
bench('markdown-exit parse', () => {
markdownExit.parse(sampleMarkdown, {})
})

bench('comark parse', async () => {
await comark(sampleMarkdown)
})

bench('comark parse no close', async () => {
await comarkNoClose(sampleMarkdown)
})

// Benchmark: markdown-it render
bench('markdown-it render', () => {
markdownIt.render(sampleMarkdown)
})

// Benchmark: markdown-exit render
bench('markdown-exit render', () => {
markdownExit.render(sampleMarkdown)
})

// Benchmark: comark parse + renderHTMLForTest
bench('comark parse + renderHTMLForTest', async () => {
const tree = await comark(sampleMarkdown)
renderHTMLForTest(tree)
})

bench('comark parse streaming', async () => {
await comarkStreaming(sampleMarkdown, { streaming: true })
barplot(() => {
group('parse', () => {
// Benchmark: markdown-it parsing
bench('markdown-it parse', () => {
markdownIt.parse(sampleMarkdown, {})
})

// Benchmark: markdown-exit parsing
bench('markdown-exit parse', () => {
markdownExit.parse(sampleMarkdown, {})
})

bench('comark parse', async () => {
await comark(sampleMarkdown)
})

bench('comark parse no close', async () => {
await comarkNoClose(sampleMarkdown)
})

bench('comark parse streaming', async () => {
await comarkStreaming(sampleMarkdown, { streaming: true })
})
})
})

// Run all benchmarks
Expand Down
111 changes: 111 additions & 0 deletions benchmarks/comark-render.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { bench, run, barplot, group } from 'mitata'
import MarkdownIt from 'markdown-it'
import MarkdownExit from 'markdown-exit'
import pluginMdc from '@comark/markdown-it'
import { createParse } from 'comark'
import { renderHTML } from '../packages/comark-html/src/index.ts'

// Sample markdown content to test with
const sampleMarkdown = `---
title: Benchmark Test
---

# Hello World

This is a **markdown** document with *italic* text and [links](https://example.com).

## Features

- List item 1
- List item 2
- List item 3

### Code Block

\`\`\`javascript
const hello = 'world'
console.log(hello)
\`\`\`

### Tables

| Header 1 | Header 2 | Header 3 |
|----------|----------|----------|
| Cell 1 | Cell 2 | Cell 3 |
| Cell 4 | Cell 5 | Cell 6 |

### MDC Components

::alert{type="info"}
This is an alert component
::

::card{title="My Card"}
Card content here
::

### More Content

1. Numbered list
2. Another item
3. Final item

> This is a blockquote with some **bold** text

~~Strikethrough text~~

`

// Initialize markdown-it with MDC plugin
const markdownIt = new MarkdownIt({
html: true,
linkify: true,
})
.enable(['table', 'strikethrough'])
.use(pluginMdc)

// Initialize markdown-exit with MDC plugin
const markdownExit = new MarkdownExit({
html: true,
linkify: true,
})
.enable(['table', 'strikethrough'])
.use(pluginMdc)

const comark = createParse()
const comarkNoClose = createParse({ autoClose: false })
const comarkStreaming = createParse()

barplot(() => {
group('render', () => {
// Benchmark: markdown-it render
bench('markdown-it render', () => {
markdownIt.render(sampleMarkdown)
})

// Benchmark: markdown-exit render
bench('markdown-exit render', () => {
markdownExit.render(sampleMarkdown)
})

// Benchmark: comark parse + renderHTML
bench('comark parse + renderHTML', async () => {
const tree = await comark(sampleMarkdown)
renderHTML(tree)
})

bench('comark parse no close', async () => {
const tree = await comarkNoClose(sampleMarkdown)
renderHTML(tree)
})

bench('comark parse streaming', async () => {
const tree = await comarkStreaming(sampleMarkdown, { streaming: true })
renderHTML(tree)
})
})
})

// Run all benchmarks
console.log('🏃 Running benchmarks...\n')
await run()
144 changes: 144 additions & 0 deletions benchmarks/plugin-punctuation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { bench, run, group, barplot } from 'mitata'
import MarkdownExit from 'markdown-exit'
import pluginMdc from '@comark/markdown-it'
import { createParse } from 'comark'
import { log } from '@comark/ansi'
import punctuation from '../packages/comark/src/plugins/punctuation'

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

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

const medium = `
# Smart Typography

"She said 'hello' to the group" --- and then left... That's all (c) 2025.

Pages 10--20 cover the topic. The product(tm) is great.

Don't forget: it's a "beautiful" day. The tolerance is +-5%.

Copyright (c) 2025 Acme Corp(r). All rights reserved...

Really?.... wow!!!!! hmm,, ok
`

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

"Paragraph ${i + 1}" has some 'quoted text' and contractions like don't, won't, can't.

The range is ${i}--${i + 10} and the em-dash --- is used here... plus (c) (r) (tm) +-5%.

Another line with "double quotes" and 'single quotes' and more ellipsis...

Really???? Wow!!!!! hmm,, ok?.... end
`).join('\n')

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

const parserTypographer = new MarkdownExit({
html: false,
linkify: true,
typographer: true,
})
.enable(['table', 'strikethrough'])
.use(pluginMdc)

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

// ── 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)
})
})
})

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)
})
})
})

// ── 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')
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`)
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
"dev:highlight": "pnpm --filter comark-vue-vite-highlight run dev",
"dev:twoslash": "pnpm --filter comark-vue-vite-twoslash run dev",
"dev:json-render": "pnpm --filter comark-vue-vite-json-render run dev",
"benchmark": "pnpm --filter comark run benchmark",
"docs": "nuxt dev --extends docus docs",
"prepack": "pnpm build && pnpm sync-plugins",
"stub": "pnpm --parallel --filter './packages/**' run stub && pnpm sync-plugins",
Expand All @@ -44,13 +43,19 @@
"postinstall": "pnpm stub"
},
"devDependencies": {
"@comark/ansi": "workspace:*",
"@comark/markdown-it": "catalog:",
"@nuxt/eslint-config": "catalog:",
"@release-it/conventional-changelog": "catalog:",
"@stylistic/eslint-plugin": "catalog:",
"@types/node": "catalog:",
"@vitejs/plugin-vue": "catalog:",
"comark": "workspace:*",
"eslint": "catalog:",
"eslint-plugin-svelte": "catalog:",
"markdown-exit": "catalog:",
"markdown-it": "catalog:",
"mitata": "catalog:",
"nuxt": "catalog:",
"playwright": "catalog:",
"release-it": "catalog:",
Expand All @@ -59,6 +64,7 @@
"remark-parse": "catalog:",
"remark-rehype": "catalog:",
"scule": "catalog:",
"tsx": "catalog:",
"typescript": "catalog:",
"unified": "catalog:"
},
Expand Down
1 change: 0 additions & 1 deletion packages/comark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@
"github-slugger": "catalog:",
"hast-util-to-string": "catalog:",
"minimark": "catalog:",
"mitata": "catalog:",
"tsx": "catalog:",
"twoslash": "catalog:",
"vitest": "catalog:"
Expand Down
Loading
Loading