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.