API Reference

Parse API

Comark provides functions to convert Comark (Components in Markdown) content into an Abstract Syntax Tree (AST).

parse(source, options?)

Parses Comark content from a string and returns the complete parsed structure.

Parameters:

  • source - The markdown/Comark content as a string
  • options? - Parser options including plugins

Returns: ComarkTree object containing:

  • nodes - The parsed Comark AST nodes
  • frontmatter - Frontmatter data parsed from YAML
  • meta - 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)
For full type definitions, see ComarkNode in the Comark AST reference.

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)

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)

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)

To disable HTML parsing and treat tags as plain text, set html: false:

parse.ts
const result = await parse(content, { html: false })

Summary

Summary extraction requires the summary plugin.

Content before the <!-- more --> comment is extracted as a summary when using the summary plugin:

parse.ts
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 as parse())

Returns: An async parser function (source: string) => Promise<ComarkTree>

Example:

parse.ts
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.

build.ts
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

server.ts
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
benchmark.ts
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:

OptionTypeDefaultDescription
autoUnwrapbooleantrueRemove unnecessary <p> wrappers from single-element containers
autoClosebooleantrueAuto-close incomplete markdown syntax
htmlbooleantrueParse embedded HTML tags into AST nodes
pluginsComarkPlugin[][]Array of plugins to apply