Skip to content

Plugins ​

Generative DOM's core is a plugin host. All markdown syntax -- headings, bold, lists, code blocks -- is implemented as plugins. This guide explains the plugin system architecture and how to work with plugins.

Registering Plugins ​

Plugins are passed to the GenerativeDom constructor as an array:

ts
import { GenerativeDom } from '@generative-dom/core';
import { markdownBase, markdownInline, markdownHeading } from '@generative-dom/plugins';

const md = new GenerativeDom({
  container: document.getElementById('output'),
  plugins: [markdownBase(), markdownInline(), markdownHeading()],
});

Each plugin is a factory function that returns an GenerativeDomPlugin object. The factory pattern allows plugins to accept configuration options.

Priority System ​

Plugins declare a priority number. Lower numbers are matched first during tokenization. This determines which plugin gets first chance to handle each position in the buffer.

RangePurposeExamples
0--99Custom syntaxevents (40), interactive (45), custom-elements (50), highlight (95)
100--199Block-level markdownmarkdown-heading (100), markdown-code (100), markdown-list (100)
200--299Inline markdownmarkdown-inline (200), markdown-link (200)
300+Fallbackmarkdown-base (300)

When two plugins have the same priority, registration order breaks the tie (first registered wins).

Dynamic Registration ​

Plugins can be added after construction:

ts
const md = new GenerativeDom({
  container: el,
  plugins: [markdownBase()],
});

// Add more plugins later
md.register(markdownHeading());
md.register(markdownInline());

Dynamic registration triggers init() on the new plugin and re-sorts the plugin list by priority. Tokens not yet rendered may be re-matched against the new plugin.

Plugins cannot be removed after construction. To change the plugin set, call destroy() and create a new instance.

Plugin Lifecycle ​

MethodWhen CalledRequired
init(ctx)Once, during registrationOptional
matchBlock(buffer, pos)Each tokenization cycle, per buffer positionOptional
matchInline(text, pos)During inline parsing within blocksOptional
render(token, ctx)Once per token during DOM constructionRequired
cleanup(element)When a DOM element is recycled or removedOptional
destroy()Once, on GenerativeDom.destroy()Optional

A plugin without at least one matchBlock or matchInline method will never produce tokens and effectively does nothing.

Plugin Configuration ​

Plugins accept options through their factory functions:

ts
import { highlight } from '@generative-dom/plugins';

// Plugin-specific config is the factory function's concern
const highlightPlugin = highlight({
  languages: ['javascript', 'typescript', 'html'],
  theme: 'dark',
});

The core never inspects these options. It only sees the returned GenerativeDomPlugin object.

Error Boundaries ​

The core wraps every plugin method call in a try/catch:

ts
try { plugin.matchBlock(buffer, pos) } catch (e) { log(e); return null; }
try { plugin.render(token, ctx) }      catch (e) { log(e); return createText('[render error]'); }
try { plugin.cleanup(element) }        catch (e) { log(e); /* continue */ }
try { plugin.destroy() }               catch (e) { log(e); /* continue */ }

A broken plugin never crashes the entire Generative DOM instance. Errors are logged with the plugin name via console.error.

Composing Plugin Sets ​

Since plugins are just objects, you can compose sets for different use cases:

ts
// Minimal set for plain text with headings
const minimal = [markdownBase(), markdownHeading()];

// Full markdown support
const full = [
  markdownBase(),
  markdownInline(),
  markdownHeading(),
  markdownCode(),
  markdownQuote(),
  markdownList(),
  markdownLink(),
];

// With syntax highlighting
const withHighlight = [...full, highlight()];

See Writing Plugins for a step-by-step guide to creating your own plugin.