Parse API
parse(source, options?)
Parses Comark content from a string and returns the complete parsed structure.
Parameters:
source- The markdown/Comark content as a stringoptions?- Parser options including plugins
Returns: ComarkTree object containing:
nodes- The parsed Comark AST nodesfrontmatter- Frontmatter data parsed from YAMLmeta- Additional metadata from plugins (e.g.,toc,summary)
Example:
import { parse } from 'comark'
const content = `---
title: Hello World
---
This is a simple example
`
const result = await parse(content)
console.log(result){
"nodes": [
["p", {}, "This is a simple example"]
],
"frontmatter": {
"title": "Hello World"
},
"meta": {}
}Frontmatter
The parse function automatically extracts and parses YAML frontmatter:
const content = `---
title: My Document
tags:
- javascript
- markdown
author:
name: John Doe
email: john@example.com
---
# Content here
`
const result = await parse(content)
console.log(result.frontmatter){
"title": "My Document",
"tags": ["javascript", "markdown"],
"author": { "name": "John Doe", "email": "john@example.com" }
}Table of Contents
The parse function automatically generates a table of contents based on headings:
const content = `# Main Title
## Section 1
Some content here.
### Subsection 1.1
More content.
## Section 2
Final content.
`
const result = await parse(content)
console.log(result.meta.toc){
"title": "Main Title",
"depth": 2,
"searchDepth": 2,
"links": [
{ "id": "section-1", "text": "Section 1", "depth": 2, "children": [
{ "id": "subsection-11", "text": "Subsection 1.1", "depth": 3 }
]},
{ "id": "section-2", "text": "Section 2", "depth": 2 }
]
}HTML Parsing
HTML tags embedded in Comark content are parsed into AST nodes by default and can be mixed freely with Comark components and markdown syntax.
const content = `
<div class="note">
::alert{type="info"}
Hello <strong class="text-red-500">world</strong>
::
</div>
`
const result = await parse(content)
console.log(result.nodes)[
["div", { "class": "note" },
["alert", { "type": "info" },
"Hello ",
["strong", { "class": "text-red-500" }, "world"]
]
]
]To disable HTML parsing and treat tags as plain text, set html: false:
const result = await parse(content, { html: false })Summary
Content before the <!-- more --> comment is extracted as a summary when using the summary plugin:
import { parse } from 'comark'
import summary from 'comark/plugins/summary'
const content = `# Article Title
This is the introduction paragraph that will be used as a summary.
<!-- more -->
This is the full article content that won't appear in the summary.
`
const result = await parse(content, {
plugins: [summary()]
})
console.log(result.meta.summary)
// ComarkNode[] with only the content before <!-- more -->createParse(options?)
Creates a reusable parser function with pre-configured options. Unlike parse() which creates a new parser instance on each call, createParse() returns a parser function that can be called multiple times with the same configuration.
Parameters:
options?- Parser options (same asparse())
Returns: An async parser function (source: string) => Promise<ComarkTree>
Example:
import { createParse } from 'comark'
import highlight from 'comark/plugins/highlight'
import githubLight from '@shikijs/themes/github-light'
import githubDark from '@shikijs/themes/github-dark'
import emoji from 'comark/plugins/emoji'
import toc from 'comark/plugins/toc'
// Create a parser with specific configuration
const parse = createParse({
autoUnwrap: true,
autoClose: true,
plugins: [
highlight({
themes: { light: githubLight, dark: githubDark }
}),
emoji(),
toc()
]
})
// Reuse the parser for multiple documents
const doc1 = await parse('# Document 1\n\nContent...')
const doc2 = await parse('# Document 2\n\nMore content...')
const doc3 = await parse('# Document 3\n\nEven more...')Use Cases
Here are some use cases for createParse():
Static Site Generator
Use Promise.all to parse all files in parallel — since createParse() initializes the parser once, the returned function is safe to call concurrently.
import { createParse } from 'comark'
import { readdir, readFile, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import { renderHTML } from '@comark/html'
import highlight from 'comark/plugins/highlight'
import githubLight from '@shikijs/themes/github-light'
import githubDark from '@shikijs/themes/github-dark'
import toc from 'comark/plugins/toc'
import emoji from 'comark/plugins/emoji'
async function buildSite(contentDir: string, outDir: string) {
// Create parser once with all desired plugins
const parse = createParse({
plugins: [
highlight({
themes: { light: githubLight, dark: githubDark }
}),
toc({ depth: 3 }),
emoji()
]
})
const files = await readdir(contentDir)
const mdFiles = files.filter(f => f.endsWith('.md'))
// Parse all files in parallel with the same parser instance
await Promise.all(
mdFiles.map(async (file) => {
const content = await readFile(join(contentDir, file), 'utf-8')
const tree = await parse(content)
const html = await renderHTML(tree)
await writeFile(join(outDir, file.replace('.md', '.html')), html)
})
)
console.log(`Built ${mdFiles.length} pages`)
}
await buildSite('./content', './dist')API Server
import { createParse } from 'comark'
import security from 'comark/plugins/security'
// Create parser once when server starts
const parse = createParse({
plugins: [
security() // Sanitize user-generated content
]
})
// Reuse parser for every request
app.post('/api/markdown', async (req, res) => {
try {
const tree = await parse(req.body.content)
res.json({ success: true, tree })
} catch (error) {
res.status(400).json({ error: 'Invalid markdown' })
}
})Benchmark
Using createParse() has several benefits over calling parse() multiple times:
- Performance: Parser and plugins are initialized once, not on every parse
- Consistency: All documents parsed with the same configuration
- Memory efficiency: Single parser instance handles multiple documents
- Ideal for batch processing: Perfect when parsing many files
import { parse, createParse } from 'comark'
import highlight from 'comark/plugins/highlight'
const content = '```js\nconsole.log("hello")\n```'
// ❌ Slow: Creates new parser + highlighter for each parse
console.time('parse x1000')
for (let i = 0; i < 1000; i++) {
await parse(content, {
plugins: [highlight()]
})
}
console.timeEnd('parse x1000')
// → ~8000ms (parser + highlighter recreated 1000 times)
// ✅ Fast: Reuses same parser + highlighter instance
console.time('createParse x1000')
const parse = createParse({
plugins: [highlight()]
})
for (let i = 0; i < 1000; i++) {
await parse(content)
}
console.timeEnd('createParse x1000')
// → ~800ms (10x faster! parser + highlighter created once)Options
Both parse() and createParse() accept the same ParseOptions:
| Option | Type | Default | Description |
|---|---|---|---|
autoUnwrap | boolean | true | Remove unnecessary <p> wrappers from single-element containers |
autoClose | boolean | true | Auto-close incomplete markdown syntax |
html | boolean | true | Parse embedded HTML tags into AST nodes |
plugins | ComarkPlugin[] | [] | Array of plugins to apply |