Headless parsing
Parse Markdown into text and a syntax tree without creating a reading component.
Use @quirekit/incremark-core to analyze Markdown or supply a syntax tree to your own renderer. This entry imports neither Vue nor UI and creates no editable view. It currently still depends on CodeMirror modules through MSFE.
1. Parse a complete text
When all text has arrived, call parseMarkdown():
import { parseMarkdown } from '@quirekit/incremark-core'
const state = parseMarkdown('# Hello')
const text = state.doc.toString()
state is a CodeMirror EditorState used read-only. It contains text, a syntax tree and extension-maintained semantics; it is neither HTML nor mdast/hast. The resulting text is still # Hello. Persist the original Markdown rather than serializing EditorState.
2. Receive text over time
Retain one MarkdownStream and pass each new piece to append():
import { MarkdownStream } from '@quirekit/incremark-core'
const stream = new MarkdownStream()
stream.append('# Hello')
const complete = stream.advance(4)
const snapshot = stream.state
append() takes only the newly received portion. If the application already accumulates the full text, use stream.update(fullText) instead. advance(4) gives the parser approximately four milliseconds of extra budget. It returns true when syntax and reference grouping for the current text are complete; otherwise the application schedules another call.
The class owns no timers. Do not spin synchronously until it returns true, which would block the interface. The budget is a cooperative hint, not a strict four-millisecond limit; reference checks and interface rendering also take time. You can read snapshot while parsing is pending, but it is not yet a final result.
3. Read the syntax tree
Get the Lezer tree from a completed state:
import { syntaxTree } from '@quirekit/cm-language'
const tree = syntaxTree(state)
const description = tree.toString()
For # Hello, the compact description is Document(ATXHeading1(HeaderMark)): a document containing a level-one heading and its # marker. Use the tree's cursor, firstChild and nextSibling APIs to traverse node kinds and UTF-16 ranges.
state.doc uses normalized newlines. Extensions maintain cross-block relationships such as links and footnotes; a source prefix alone does not establish a final link target. Tree descriptions are useful for debugging, not persistence.
Complete function
recipes/snapshot.ts combines steps one and three as inspectMarkdown(source), returning the state, text and tree description. Consult this single-file function when integrating an analysis tool:
import { parseMarkdown } from "@quirekit/incremark-core";
import { syntaxTree } from "@quirekit/cm-language";
/**
* Parse once without mounting an editor or creating Vue components.
* @param source - Exact Markdown supplied by the host.
* @returns The immutable snapshot, canonical text and a compact syntax-tree description.
*/
export function inspectMarkdown(source: string) {
const state = parseMarkdown(source);
return { state, text: state.doc.toString(), syntax: syntaxTree(state).toString() };
}