API Reference

Parse API

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

String Parsing

parse(source: string): ComarkTree

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
description: A simple example
---

# Hello World

This is a **markdown** document with *Comark* components.

::alert{type="info"}
This is an alert component
::
`

const result = await parse(content)

console.log(result.frontmatter)

Reusable Parser

createParse(options?): (source: string) => Promise<ComarkTree>

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.

Benefits:

  • 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

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 Case: Static Site Generator

build.ts
import { createParse } from 'comark'
import { readdir, readFile, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import { renderHTML } from 'comark/string'
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 with the same parse instance
  for (const file of mdFiles) {
    const content = await readFile(join(contentDir, file), 'utf-8')
    const tree = await parse(content)
    const html = renderHTML(tree)

    await writeFile(
      join(outDir, file.replace('.md', '.html')),
      html
    )
  }

  console.log(`Built ${mdFiles.length} pages`)
}

await buildSite('./content', './dist')

Use Case: 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' })
  }
})

Performance Comparison

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)

Types

ComarkTree

The main return type for all parse functions.

interface ComarkTree {
  nodes: ComarkNode[]          // The parsed AST nodes
  frontmatter: Record<string, any>  // YAML frontmatter data
  meta: {
    toc?: any                  // Table of contents (from toc plugin)
    summary?: ComarkNode[]     // Summary content (from summary plugin)
    [key: string]: any         // Other plugin metadata
  }
}

ComarkNode

type ComarkNode = ComarkElement | ComarkText

type ComarkText = string

type ComarkElement = [string, ComarkElementAttributes, ...ComarkNode[]]

type ComarkElementAttributes = {
  [key: string]: unknown
}

Processing Multiple Files

For better performance when processing multiple files, use createParse() instead of parse():

build.ts
import { readdir, readFile } from 'node:fs/promises'
import { join } from 'node:path'
import { createParse } from 'comark'
import highlight from 'comark/plugins/highlight'
import githubLight from '@shikijs/themes/github-light'
import githubDark from '@shikijs/themes/github-dark'

async function processMarkdownDirectory(dir: string) {
  const files = await readdir(dir)
  const mdFiles = files.filter(f => f.endsWith('.md'))

  // Create parser once
  const parse = createParse({
    plugins: [
      highlight({
        themes: { light: githubLight, dark: githubDark }
      })
    ]
  })

  // Reuse parser for all files
  const results = await Promise.all(
    mdFiles.map(async (file) => {
      const content = await readFile(join(dir, file), 'utf-8')
      const result = await parse(content)
      return { file, result }
    })
  )

  return results
}

// Usage
const results = await processMarkdownDirectory('./content')
console.log(`Processed ${results.length} files`)

Frontmatter Parsing

The parse functions automatically extract and parse 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 functions automatically generate 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)

Summary Extraction

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

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

Error Handling

parse.ts
import { parse } from 'comark'
import { readFile } from 'node:fs/promises'

try {
  const content = await readFile('content.md', 'utf-8')
  const result = await parse(content)
  console.log('Parsed successfully:', result.nodes)
}
catch (error) {
  console.error('Parse error:', error)
}

See Also

Copyright © 2026