High-performance CommonMark parser for Synth - 26-42x faster than remark
npm install @sylphx/synth-mdHigh-performance CommonMark parser - 26-42x faster than remark.
- Ultra-Fast Parsing: 26-42x faster than remark/unified
- CommonMark Compliant: Full CommonMark specification support
- Streaming: Process Markdown incrementally
- Incremental: Re-parse only changed regions
- Plugin System: Extensible with transforms and visitors
- Zero Dependencies: Self-contained implementation with no external parser dependencies
``
Benchmark Results (Complex Document - 10KB):
─────────────────────────────────────────────
remark (unified) 238 Hz 4.20 ms
@sylphx/synth-md 10,045 Hz 0.10 ms
🚀 42.2x faster than remark
`
`bash`
npm install @sylphx/synth @sylphx/synth-md
`bashGitHub Flavored Markdown (tables, strikethrough, autolinks, task lists)
npm install @sylphx/synth-md-gfm
Usage
$3
`typescript
import { parse } from '@sylphx/synth-md'const tree = parse('# Hello World')
`$3
`typescript
import { parse, addHeadingIds, tableOfContents } from '@sylphx/synth-md'// Sync plugins
const tree = parse(text, {
plugins: [addHeadingIds, tableOfContents]
})
// Access plugin data
console.log(tree.meta.data.toc)
`$3
`typescript
import { parseAsync } from '@sylphx/synth-md'// Async plugins or prefer async/await
const tree = await parseAsync(text, {
plugins: [asyncPlugin1, asyncPlugin2]
})
`$3
`typescript
import { createParser } from '@sylphx/synth-md'const parser = createParser()
.use(addHeadingIds)
.use(tableOfContents)
// Plugins automatically applied
const tree1 = parser.parse(doc1)
const tree2 = parser.parse(doc2)
// Or add one-off plugins
const tree3 = parser.parse(doc3, {
plugins: [extraPlugin] // Merged with registered plugins
})
`$3
`typescript
import { parse } from '@sylphx/synth-md'const tree = parse(largeDocument, {
useBatchTokenizer: true, // 4-5x faster on large docs
useNodePool: true, // 10-13x faster for repeated parses (default: true)
batchSize: 32, // Optimal batch size
buildIndex: false // Skip index for 4x speedup (default)
})
`$3
`typescript
import { IncrementalMarkdownParser, detectEdit } from '@sylphx/synth-md'const incParser = new IncrementalMarkdownParser()
incParser.parse(originalText)
// After edit
const edit = detectEdit(originalText, newText)
const updated = incParser.update(newText, edit) // 10-100x faster
`$3
`typescript
import { parseStream, parseWithProgress } from '@sylphx/synth-md'
import { createReadStream } from 'fs'// From file stream
const stream = createReadStream('large.md', { encoding: 'utf8' })
const tree = await parseStream(stream)
// With progress tracking
const tree = await parseWithProgress(largeText, (progress) => {
console.log(
Parsed ${progress.percent}%)
})// Manual streaming
import { StreamingMarkdownParser } from '@sylphx/synth-md'
const parser = new StreamingMarkdownParser()
parser.on('node', (node) => console.log('Node:', node.type))
parser.on('end', (tree) => console.log('Done:', tree))
parser.write('# Hello\n')
parser.write('\nWorld')
await parser.end()
`API
$3
####
parse(text: string, options?: ParseOptions): TreeSynchronous parsing with optional plugins (sync only).
`typescript
const tree = parse('# Hello', { plugins: [addHeadingIds] })
`####
parseAsync(text: string, options?: ParseOptions): PromiseAsync parsing with support for async plugins.
`typescript
const tree = await parseAsync(text, { plugins: [asyncPlugin] })
`####
createParser(): ParserCreate reusable parser instance with plugin registration.
`typescript
const parser = createParser()
.use(plugin1)
.use(plugin2)const tree = parser.parse(text)
`####
parseStream(stream: AsyncIterableParse from a readable stream (ideal for large files).
`typescript
import { createReadStream } from 'fs'const stream = createReadStream('large.md', { encoding: 'utf8' })
const tree = await parseStream(stream)
`####
parseWithProgress(text: string, onProgress: ProgressCallback, options?: StreamingOptions): PromiseParse with progress tracking.
`typescript
const tree = await parseWithProgress(text, (progress) => {
console.log(${progress.percent}% complete (${progress.processed}/${progress.total} bytes))
})
`$3
-
parse(text, options) - Sync parse (auto-applies registered plugins)
- parseAsync(text, options) - Async parse (auto-applies registered plugins)
- use(plugin) - Register plugin (chainable)
- getIndex() - Get query index (if built)$3
`typescript
interface ParseOptions {
buildIndex?: boolean // Build query index (default: false)
plugins?: Plugin[] // Plugins to apply
useNodePool?: boolean // Object pooling (default: true)
useBatchTokenizer?: boolean // Batch processing (default: false)
batchSize?: number // Batch size (default: 16, range: 1-128)
}
`$3
-
addHeadingIds - Add slugified IDs to headings
- tableOfContents - Generate table of contents
- uppercaseHeadings - Uppercase all heading text
- addCodeLineNumbers - Add line numbers to code blocks
- removeComments - Remove HTML comments
- wrapParagraphs - Wrap paragraphs with metadata$3
-
IncrementalMarkdownParser - Incremental updates (10-100x faster for edits)
- StreamingMarkdownParser - Streaming parsingSupported Markdown
$3
- Headings (ATX and Setext)
- Paragraphs
- Block quotes
- Lists (ordered and unordered)
- Code blocks (fenced and indented)
- Horizontal rules
- Links and images
- Emphasis and strong
- Inline code
- HTML blocks$3
- Tables
- Strikethrough
- Task lists
- AutolinksPerformance Tips
1. For Large Documents (>10KB): Enable batch tokenizer
`typescript
parse(text, { useBatchTokenizer: true, batchSize: 32 })
`2. For Repeated Parses: Node pooling is ON by default (1.5-2x faster)
`typescript
parse(text, { useNodePool: true }) // Already default!
`3. For Edits: Use incremental parser (10-100x faster)
`typescript
const incParser = new IncrementalMarkdownParser()
// ... later
incParser.update(newText, edit)
`4. For Live Preview: Use streaming parser
`typescript
const stream = new StreamingMarkdownParser()
stream.feed(chunk1)
stream.feed(chunk2)
const tree = stream.end()
`5. For Queries: Only enable index when needed
`typescript
parse(text, { buildIndex: true }) // 4x slower, but enables queries
`6. Reuse Parser Instances: Amortize initialization cost
`typescript
const parser = createParser().use(myPlugin)
docs.forEach(doc => parser.parse(doc)) // Reuse same parser
``MIT
---