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