GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||
GeezerVison is showing comark.dev — Parse API API Reference Parse APIParse Markdown into a compact, serializable document with parseMarkdown() or createMarkdownParser(), on the server, in the browser, or from a stream.
|
| Import | Contents |
|---|---|
comark | parseMarkdown(), createMarkdownParser(), createSerializedMarkdownParser() with the default plugins and auto-close, plus autoCloseMarkdown(), createComarkContext(), defineComarkPlugin(), parseFrontmatter() |
comark/core | The same three parser functions with no default plugins and no built-in auto-closer |
comark/auto-close | autoCloseMarkdown() and its placeholders, without the parser |
comark/context | createComarkContext() and the live-update types, without the parser |
comark/parse | Deprecated. Alias of comark. Import from comark instead |
comark/coreUse comark/core when you want to choose every plugin yourself and keep the default plugins out of your bundle. It accepts the same options as comark, with two differences:
plugins run. registerDefaultPlugins is not accepted; add linkify() to link bare URLs.autoClose is a function or undefined (the default, which skips auto-closing). Pass autoCloseMarkdown from comark/auto-close to opt in.import { createMarkdownParser } from 'comark/core'
import { autoCloseMarkdown } from 'comark/auto-close'
import components from 'comark/plugins/components'
const parse = createMarkdownParser({
plugins: [components()],
autoClose: (markdown, streaming) => autoCloseMarkdown(markdown, { dropTrailingOpeners: streaming }),
})
const tree = await parse('::alert\n**Streaming')Both parseMarkdown() and createMarkdownParser() accept the same ParserOptions:
| Option | Type | Default | Description |
|---|---|---|---|
autoUnwrap | boolean | true | Remove unnecessary <p> wrappers from single-element containers |
autoClose | boolean | (markdown: string, streaming?: boolean) => string | true | Auto-close incomplete markdown syntax, or use a custom completion function. streaming is true when the parse was called with { streaming: true }. |
unwrap | boolean | string | string[] | false | Remove wrapper tags from the tree, hoisting their children (MDC unwrap behaviour). true unwraps p; a comma/whitespace-separated string or array unwraps the listed tags; '*' matches any tag. Tags apply sequentially (each descends one level), and adjacent text is merged into a single string. |
| linkify | boolean | true | Deprecated. Use registerDefaultPlugins: false and register linkify() only when needed. Still honored (over registerDefaultPlugins) and logs a warning |
| headingIds | boolean \| 'nested' \| 'flat' | true | Auto-generate id attributes for h1–h6 from visible heading text (not tag or component names). true/'flat' uses the heading's own text only (install); 'nested' prefixes each id with its parent heading's id (setup-install); false disables auto ids. Unicode letters, marks, and numbers are kept (## Café → café); punctuation and symbols are dropped, and a heading that slugifies to nothing gets no id. Ids are unique per document: duplicates get a -1, -2, ... suffix (options, options-1, options-2). An explicit id attribute wins. See Headings. |
| registerDefaultPlugins | boolean | true | Register the built-in default plugins (linkify, frontmatter, html, alert, task-list, components, attributes). Set false to disable them. |
| plugins | ComarkPlugin[] | [] | Ordered plugins to run after the defaults. A same-name plugin replaces its default; duplicate explicit names keep the first instance. See Default plugins. |
| tracer | ComarkTracer | undefined | Timing recorder for the parse pipeline — see Timing the parse |
Pass a tracer to time each phase of the pipeline and every plugin hook, so you can see where parse time goes (for example, a slow post shiki hook). The contract is a structural subset of OpenTelemetry Tracer — startSpan and startActiveSpan — so a real OTel tracer works as-is:
import { trace } from '@opentelemetry/api'
import shiki from 'comark/plugins/shiki'
const parse = createMarkdownParser({
// Uses the OpenTelemetry provider registered by your app or hosting platform.
tracer: trace.getTracer('comark'),
plugins: [shiki()],
})@opentelemetry/api defines the tracing API but does not export spans by itself. trace.getTracer() uses the globally registered OpenTelemetry provider; without one, it returns a no-op tracer. Hosting platforms often register and configure that provider for you. Otherwise, initialize an OpenTelemetry SDK and exporter before creating the parser.See the complete Node.js example, including an OTLP/HTTP exporter and commands for viewing traces locally with otel-front.
Or a minimal recorder:
const spans: { name: string, duration: number }[] = []
const tracer = {
startSpan(name) {
const start = performance.now()
return { end: () => spans.push({ name, duration: performance.now() - start }) }
},
startActiveSpan(name, optionsOrFn, maybeFn) {
const fn = typeof optionsOrFn === 'function' ? optionsOrFn : maybeFn
const span = tracer.startSpan(name)
// Nested startActiveSpan/startSpan calls become children via your context/stack.
return fn(span) // caller (comark) ends the span
},
}
const parse = createMarkdownParser({ tracer, plugins: [shiki()] })
await parse(markdown)
// spans → comark:parse
// ├─ comark:autoclose
// ├─ comark:pre:frontmatter
// ├─ comark:tokenize
// ├─ comark:nodes
// ├─ comark:post:alert
// └─ comark:post:shiki
// …Recorded spans: a root comark:parse active span enclosing comark:autoclose, comark:tokenize (markdown parsing), comark:nodes (token → AST conversion and unwrapping), and comark:pre:<name> / comark:post:<name> for each plugin hook. Nested startActiveSpan calls form the parent → child hierarchy (OTel active context, or a stack in a simple recorder). There is no timing overhead when tracer is omitted, and no Node-specific API is used — it works in the browser too.
Use unwrap: 'p' (or unwrap: true) to render markdown without the
wrapping <p>, which is handy for buttons, badges, and other inline hosts.
Adjacent text is merged into a single string, matching MDC's unwrap:
await parseMarkdown('Hello **world**', { unwrap: 'p' })
// nodes: ['Hello ', ['strong', {}, 'world']]
await parseMarkdown('a\n\nb', { unwrap: true })
// nodes: ['ab'] (paragraphs merged, no separator)Tags are applied sequentially, each descending one level into the result of
the previous one — so a space-separated (or comma-separated) list peels nested
wrappers. '*' matches any tag:
// Unwrap the <ul>, then the <li> inside it
await parseMarkdown('- Buy milk', { unwrap: 'ul li' })
// nodes: ['Buy milk']On the framework components this is exposed as an unwrap shorthand prop:
<UButton><Markdown :value="text" unwrap /></UButton>
<UButton><Markdown :value="text" unwrap="ul li" /></UButton>
<UButton><Markdown :value="text" :options="{ unwrap: 'p' }" /></UButton>