@signalwire/docusaurus-plugin-llms-txt
Version:
Generate Markdown versions of Docusaurus HTML pages and an llms.txt index file
152 lines (151 loc) • 5.55 kB
JavaScript
/**
* Simplified plugin registry for unified processors
* Follows Docusaurus pattern: before/after arrays with simple order
*/
import rehypeParse from 'rehype-parse';
import rehypeRemark from 'rehype-remark';
import remarkGfm from 'remark-gfm';
import remarkStringify from 'remark-stringify';
import { unified } from 'unified';
import rehypeLinks from './rehype-links';
import rehypeTables from './rehype-tables';
/**
* Simplified plugin registry class
*/
export class PluginRegistry {
/**
* Apply a single plugin input following unified.js conventions
*/
applyPluginInput(processor, pluginInput) {
if (typeof pluginInput === 'function') {
// Direct plugin function
processor.use(pluginInput);
}
else if (Array.isArray(pluginInput)) {
// Array format: [plugin, options?, settings?]
const [plugin, options, settings] = pluginInput;
if (settings !== undefined) {
processor.use(plugin, options, settings);
}
else if (options !== undefined) {
processor.use(plugin, options);
}
else {
processor.use(plugin);
}
}
}
/**
* Apply an array of plugins in order
*/
applyPluginArray(processor, plugins = [], logger) {
for (const pluginInput of plugins) {
try {
this.applyPluginInput(processor, pluginInput);
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
if (logger) {
logger.warn(`Failed to apply user plugin: ${errorMessage}`);
}
// Continue processing other plugins
}
}
}
/**
* Apply built-in rehype plugins in fixed order
*/
applyBuiltinRehypePlugins(processor, options) {
// Apply built-in plugins in fixed order
if (options.rehypeProcessTables !== false) {
processor.use(rehypeTables);
}
if (options.rehypeProcessLinks !== false) {
const linkOptions = {
baseUrl: options.baseUrl ?? '',
relativePaths: options.relativePaths !== false,
enableMarkdownFiles: options.enableMarkdownFiles !== false,
excludeRoutes: options.excludeRoutes ?? [],
fullConfig: options.fullConfig,
routeLookup: options.routeLookup,
};
processor.use(rehypeLinks, linkOptions);
}
// Always last - converts HTML AST to Markdown AST
processor.use(rehypeRemark, {
handlers: { br: () => ({ type: 'html', value: '<br />' }) },
});
}
/**
* Apply built-in remark plugins in fixed order
*/
applyBuiltinRemarkPlugins(processor, options) {
// Apply remark-gfm if enabled
if (options.remarkGfm !== false) {
const gfmOptions = typeof options.remarkGfm === 'object' && options.remarkGfm !== null
? options.remarkGfm
: undefined;
if (gfmOptions !== undefined) {
processor.use(remarkGfm, gfmOptions);
}
else {
processor.use(remarkGfm);
}
}
}
/**
* Apply rehype (HTML processing) plugins using Docusaurus pattern
*/
applyRehypePlugins(processor, options) {
// 1. Apply "before" plugins first
this.applyPluginArray(processor, options.beforeDefaultRehypePlugins, options.logger);
// 2. Apply built-in plugins in fixed order
this.applyBuiltinRehypePlugins(processor, options);
// 3. Apply "after" plugins last
this.applyPluginArray(processor, options.rehypePlugins, options.logger);
}
/**
* Apply remark (Markdown processing) plugins using Docusaurus pattern
*/
applyRemarkPlugins(processor, options) {
// 1. Apply "before" plugins first
this.applyPluginArray(processor, options.beforeDefaultRemarkPlugins, options.logger);
// 2. Apply built-in plugins in fixed order
this.applyBuiltinRemarkPlugins(processor, options);
// 3. Apply "after" plugins last
this.applyPluginArray(processor, options.remarkPlugins, options.logger);
}
/**
* Apply stringify plugins
*/
applyStringifyPlugins(processor, options) {
const stringifyOptions = options.remarkStringify ?? {};
processor.use(remarkStringify, stringifyOptions);
}
/**
* Create a complete processor pipeline for HTML to Markdown conversion
*/
createHtmlToMarkdownProcessor(options) {
// HTML processing pipeline (hast)
const htmlProcessor = unified();
this.applyRehypePlugins(htmlProcessor, options);
// Markdown processing pipeline (mdast)
const markdownProcessor = unified();
this.applyRemarkPlugins(markdownProcessor, options);
this.applyStringifyPlugins(markdownProcessor, options);
return { htmlProcessor, markdownProcessor };
}
/**
* Create a lightweight processor for metadata extraction only
*/
createMetadataProcessor() {
const processor = unified();
// Only HTML parsing, no user plugins
processor.use(rehypeParse, { fragment: false });
return processor;
}
}
/**
* Default plugin registry instance
*/
export const defaultPluginRegistry = new PluginRegistry();