Skip to content

Commit eb56d5d

Browse files
perf(plugins): optimize highlight code processing (#162)
Co-authored-by: Farnabaz <farnabaz@gmail.com>
1 parent 4f643ef commit eb56d5d

3 files changed

Lines changed: 845 additions & 89 deletions

File tree

benchmarks/plugin-highlight.ts

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { bench, run, group, barplot } from 'mitata'
2+
import MarkdownIt from 'markdown-it'
3+
import MarkdownExit from 'markdown-exit'
4+
import pluginMdc from '@comark/markdown-it'
5+
import { createParse } from 'comark'
6+
import highlight, { getHighlighter } from '../packages/comark/src/plugins/highlight'
7+
import { codeToHast } from 'shiki/core'
8+
9+
const short = `
10+
# Quick Start
11+
12+
Install with:
13+
14+
\`\`\`bash
15+
npm install comark
16+
\`\`\`
17+
18+
Then use it:
19+
20+
\`\`\`javascript
21+
import { parse } from 'comark'
22+
const tree = await parse('# Hello')
23+
\`\`\`
24+
`
25+
26+
const medium = `
27+
# API Reference
28+
29+
## Parse
30+
31+
\`\`\`typescript
32+
import { parse } from 'comark'
33+
34+
interface ParseOptions {
35+
autoClose?: boolean
36+
streaming?: boolean
37+
plugins?: ComarkPlugin[]
38+
}
39+
40+
const tree = await parse(markdown, {
41+
autoClose: true,
42+
plugins: [highlight()],
43+
})
44+
\`\`\`
45+
46+
## Render
47+
48+
\`\`\`vue
49+
<script setup>
50+
import { Comark } from '@comark/vue'
51+
import highlight from '@comark/vue/plugins/highlight'
52+
</script>
53+
54+
<template>
55+
<Suspense>
56+
<Comark :plugins="[highlight()]">{{ content }}</Comark>
57+
</Suspense>
58+
</template>
59+
\`\`\`
60+
61+
## Configuration
62+
63+
\`\`\`json
64+
{
65+
"name": "my-project",
66+
"dependencies": {
67+
"comark": "^0.2.0",
68+
"@comark/vue": "^0.2.0"
69+
}
70+
}
71+
\`\`\`
72+
`
73+
74+
const long = Array.from({ length: 20 }, (_, i) => `
75+
## Module ${i + 1}
76+
77+
\`\`\`typescript
78+
export function module${i + 1}(input: string): string {
79+
const result = input.trim()
80+
return result.length > 0 ? result : 'default'
81+
}
82+
\`\`\`
83+
84+
\`\`\`bash
85+
npm run build:module-${i + 1}
86+
\`\`\`
87+
`).join('\n')
88+
89+
// markdown-it / markdown-exit produce flat tokens — to get syntax highlighting
90+
// they still need shiki. We benchmark both pipelines with the same shiki work.
91+
const markdownIt = new MarkdownIt({ html: true, linkify: true })
92+
.enable(['table', 'strikethrough']).use(pluginMdc)
93+
const markdownExit = new MarkdownExit({ html: true, linkify: true })
94+
.enable(['table', 'strikethrough']).use(pluginMdc)
95+
96+
// comark: baseline vs highlight plugin
97+
const comark = createParse()
98+
const comarkHl = createParse({ plugins: [highlight()] })
99+
100+
// Pre-warm shiki so we benchmark steady-state, not cold-start
101+
const shiki = await getHighlighter()
102+
103+
// Warm up comark highlight to ensure shiki languages are loaded
104+
await comarkHl(medium)
105+
106+
// Helper: extract fence tokens from markdown-it/exit and highlight them with shiki
107+
// This simulates what a real markdown-it + shiki pipeline does.
108+
async function highlightTokens(tokens: any[]) {
109+
for (const token of tokens) {
110+
if (token.type === 'fence' && token.info) {
111+
await codeToHast(shiki, token.content, {
112+
lang: token.info.split(/\s/)[0],
113+
themes: { light: 'material-theme-lighter', dark: 'material-theme-palenight' },
114+
})
115+
}
116+
}
117+
}
118+
119+
for (const [label, content] of [
120+
['short (2 blocks)', short],
121+
['medium (3 blocks)', medium],
122+
['long (40 blocks)', long],
123+
] as const) {
124+
barplot(() => {
125+
group(`highlight — ${label}`, () => {
126+
bench('comark', async () => {
127+
await comark(content)
128+
})
129+
bench('comark + highlight', async () => {
130+
await comarkHl(content)
131+
})
132+
bench('markdown-it + shiki', async () => {
133+
const tokens = markdownIt.parse(content, {})
134+
await highlightTokens(tokens)
135+
})
136+
bench('markdown-exit + shiki', async () => {
137+
const tokens = markdownExit.parse(content, {})
138+
await highlightTokens(tokens)
139+
})
140+
})
141+
})
142+
}
143+
144+
console.log('🏃 Running benchmarks...\n')
145+
await run()

0 commit comments

Comments
 (0)