Custom
Plugin API
Type-safe API for creating Comark plugins with pre/post lifecycle hooks.
The comark/parse module exports defineComarkPlugin — a typed factory wrapper for building plugins that extend the parser.
defineComarkPlugin(factory)
Wraps a plugin factory function to provide type safety for both options and the returned plugin.
Parameters:
factory- A function(options?: O) => ComarkPluginwhereOis an optional options type. When the plugin is instantiated,optionsreceives the values passed by the caller. SeeComarkPluginfor the full shape of the returned object.
Returns: A typed plugin factory (options?: O) => ComarkPlugin
Example:
import { defineComarkPlugin } from 'comark/parse'
export default defineComarkPlugin(() => ({
name: 'my-plugin',
pre(state) {},
post(state) {},
}))With typed options:
import { defineComarkPlugin } from 'comark/parse'
interface MyOptions {
prefix?: string
}
export default defineComarkPlugin((options: MyOptions = {}) => ({
name: 'my-plugin',
post(state) {
// options.prefix is typed
},
}))Lifecycle
Plugins hook into two phases of the parsing pipeline:
Markdown string
│
▼
┌──────────┐
│ pre() │ ← Modify raw markdown before parsing
└──────────┘
│
▼
Parse & Build ComarkTree
│
▼
┌──────────┐
│ post() │ ← Transform the AST after parsing
└──────────┘
│
▼
ComarkTreepre(state)
Called before markdown is tokenized. Use it to transform the raw markdown string.
import { defineComarkPlugin } from 'comark/parse'
export default defineComarkPlugin(() => ({
name: 'strip-comments',
pre(state) {
state.markdown = state.markdown.replace(/<!--[\s\S]*?-->/g, '')
},
}))ComarkParsePreState:
| Field | Type | Description |
|---|---|---|
markdown | string | The raw markdown — modify to change parser input |
options | ParseOptions | The parser configuration |
post(state)
Called after the AST is built. Use it to traverse nodes or populate meta with extracted data.
import { defineComarkPlugin } from 'comark/parse'
import { visit } from 'comark/utils'
export default defineComarkPlugin(() => ({
name: 'word-count',
post(state) {
let count = 0
visit(state.tree,
(node) => typeof node === 'string',
(node) => { count += (node as string).split(/\s+/).filter(Boolean).length }
)
state.tree.meta.wordCount = count
},
}))ComarkParsePostState:
| Field | Type | Description |
|---|---|---|
markdown | string | The original markdown input |
tree | ComarkTree | The parsed AST — modify to transform output |
options | ParseOptions | The parser configuration |
tokens | unknown[] | The raw markdown-it tokens |
ComarkPlugin
| Property | Type | Description |
|---|---|---|
name | string | A unique identifier for the plugin |
markdownItPlugins | MarkdownItPlugin[] | markdown-it plugins to register on the parser — see Markdown-it Plugins |
pre | (state: ComarkParsePreState) => void | Hook called before parsing |
post | (state: ComarkParsePostState) => void | Hook called after the AST is built |
Usage
Pass plugins to parse() or the <Comark> component:
import { parse } from 'comark'
import myPlugin from './my-plugin'
const tree = await parse(content, {
plugins: [myPlugin()]
})<script setup lang="ts">
import { Comark } from '@comark/vue'
import myPlugin from './my-plugin'
</script>
<template>
<Comark :plugins="[myPlugin()]">{{ content }}</Comark>
</template>import { Comark } from '@comark/react'
import myPlugin from './my-plugin'
<Comark plugins={[myPlugin()]}>{content}</Comark><script>
import { Comark } from '@comark/svelte'
import myPlugin from './my-plugin'
</script>
<Comark markdown={content} plugins={[myPlugin()]} />