Documentation generator for React component libraries with live demos, API tables, and theming support
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Plugin architecture for extending dumi functionality with custom tech stacks, content tabs, and asset processing through a comprehensive API system.
Register custom technology stacks for supporting additional frameworks beyond React.
/**
* Register a new tech stack for parsing and rendering demos
* @param fn - Function returning tech stack implementation
*/
interface IApi {
registerTechStack: (fn: () => IDumiTechStack) => void;
}
/**
* Abstract base class for implementing custom tech stacks
*/
abstract class IDumiTechStack {
/** Tech stack name (e.g., 'react', 'vue', 'angular') */
abstract name: string;
/** Runtime options for browser rendering */
abstract runtimeOpts?: {
/** Path to component detection function */
preflightPath?: string;
/** Path to render/unmount function */
rendererPath?: string;
/** Path to runtime compile function */
compilePath?: string;
/** Path to runtime plugin */
pluginPath?: string;
};
/**
* Check if language/node is supported by this tech stack
* @param node - HAST element node
* @param lang - Code block language
* @returns Whether this tech stack can handle the code
*/
abstract isSupported(node: Element, lang: string): boolean;
/**
* Transform demo source code for compilation
* @param raw - Raw source code
* @param opts - Transformation options
* @returns Transformed code ready for compilation
*/
abstract transformCode(raw: string, opts: {
type: 'external' | 'code-block';
fileAbsPath: string;
}): string;
/**
* Generate asset metadata for the demo
* @param asset - Base asset data
* @param opts - Generation context
* @returns Enhanced asset metadata
*/
abstract generateMetadata?(
asset: ExampleBlockAsset,
opts: {
type: 'external' | 'code-block';
mdAbsPath: string;
fileAbsPath?: string;
entryPointCode?: string;
}
): Promise<ExampleBlockAsset> | ExampleBlockAsset;
/**
* Generate previewer component props
* @param props - Base previewer props
* @param opts - Generation context
* @returns Enhanced previewer props
*/
abstract generatePreviewerProps?(
props: IDumiDemoProps['previewerProps'],
opts: {
type: 'external' | 'code-block';
mdAbsPath: string;
fileAbsPath?: string;
entryPointCode?: string;
}
): Promise<IDumiDemoProps['previewerProps']> | IDumiDemoProps['previewerProps'];
/**
* Transform code using esbuild onLoad callback
* @param args - esbuild onLoad arguments
* @returns Transformed code result or null to skip
*/
abstract onBlockLoad?(args: {
path: string;
namespace?: string;
suffix?: string;
pluginData?: any;
entryPointCode: string;
filename: string;
}): { content: string; loader: string } | null;
}Usage:
// Vue.js tech stack implementation
export default (api: IApi) => {
api.registerTechStack(() => ({
name: 'vue',
isSupported(node, lang) {
return ['vue', 'vue3'].includes(lang);
},
transformCode(raw, { type, fileAbsPath }) {
// Transform Vue SFC to executable code
return `
import { createApp } from 'vue';
const component = ${raw};
export default () => {
const app = createApp(component);
return app;
};
`;
},
runtimeOpts: {
rendererPath: require.resolve('./vue-renderer'),
compilePath: require.resolve('./vue-compiler'),
},
}));
};Add custom content tabs for enhanced page functionality.
/**
* Add a content tab to documentation pages
* @param fn - Function returning tab configuration
*/
interface IApi {
addContentTab: (fn: () => IContentTab) => void;
}
interface IContentTab {
/** Unique tab key and URL query parameter */
key: string;
/** Optional unique ID for tab conflicts */
id?: string;
/** Route pattern to apply tab (regex) */
test?: RegExp;
/** Tab display title */
title?: string;
/** Internationalization key for title */
titleIntlId?: string;
/** Path to tab React component */
component: string;
}Usage:
// Add playground tab for interactive demos
export default (api: IApi) => {
api.addContentTab(() => ({
key: 'playground',
test: /\/components\//,
title: 'Playground',
component: require.resolve('./PlaygroundTab'),
}));
};
// PlaygroundTab.tsx
import React from 'react';
import { useRouteMeta, useLiveDemo } from 'dumi/theme';
export default function PlaygroundTab() {
const routeMeta = useRouteMeta();
const { renderDemo } = useLiveDemo();
return (
<div>
<h2>Interactive Playground</h2>
{/* Custom playground implementation */}
</div>
);
}Modify and access component/demo asset metadata.
/**
* Modify assets metadata during build process
* @param fn - Function to transform assets data
*/
interface IApi {
modifyAssetsMetadata: (fn: (memo: AssetsPackage, args: null) => AssetsPackage) => void;
}
/**
* Get current assets metadata (available during build)
* @returns Promise resolving to complete assets package
*/
interface IApi {
getAssetsMetadata?: () => Promise<AssetsPackage>;
}
interface AssetsPackage {
/** Package name */
name: string;
/** Package version */
version: string;
/** Package description */
description?: string;
/** Package homepage */
homepage?: string;
/** Package keywords */
keywords?: string[];
/** Component/function assets */
assets: {
/** Atom assets by ID */
atoms: Record<string, AtomAsset>;
/** Example assets by ID */
examples: Record<string, ExampleBlockAsset>;
};
}Usage:
// Enhance asset metadata with custom information
export default (api: IApi) => {
api.modifyAssetsMetadata((assets) => {
// Add custom package information
assets.name = 'My Design System';
assets.homepage = 'https://my-design-system.com';
// Enhance component metadata
Object.values(assets.assets.atoms).forEach((atom) => {
if (atom.type === 'COMPONENT') {
// Add design system category
atom.category = getComponentCategory(atom.id);
// Add accessibility information
atom.a11y = getA11yInfo(atom.id);
}
});
return assets;
});
// Access metadata during build
api.onBuildComplete(async () => {
const assets = await api.getAssetsMetadata?.();
// Generate additional documentation files
generateComponentCatalog(assets);
generateA11yReport(assets);
});
};Modify theme components and configuration.
/**
* Modify theme data including components, layouts, and locales
* @param fn - Function to transform theme data
*/
interface IApi {
modifyTheme: (fn: (memo: IThemeLoadResult, args: null) => IThemeLoadResult) => void;
}
interface IThemeLoadResult {
/** Built-in components (API, Badge, etc.) */
builtins: Record<string, string>;
/** Layout components (DocLayout, etc.) */
layouts: Record<string, string>;
/** Slot components (Header, Footer, etc.) */
slots: Record<string, string>;
/** Internationalization messages */
locales: Record<string, {
messages: Record<string, string>;
}>;
/** Global style files */
globalStyles: string[];
/** Custom theme configuration */
[key: string]: any;
}Usage:
// Customize theme with additional components and styling
export default (api: IApi) => {
api.modifyTheme((memo) => {
// Add custom built-in components
memo.builtins.CustomAPI = require.resolve('./components/CustomAPI');
memo.builtins.DesignTokens = require.resolve('./components/DesignTokens');
// Override default slots
memo.slots.Header = require.resolve('./components/CustomHeader');
memo.slots.Footer = require.resolve('./components/CustomFooter');
// Add custom internationalization
memo.locales.en.messages.customWelcome = 'Welcome to our design system';
memo.locales.zh.messages.customWelcome = '欢迎使用我们的设计系统';
// Add global styles
memo.globalStyles.push(require.resolve('./styles/custom.less'));
return memo;
});
};Utilities for building robust plugins.
/**
* Plugin API interface extending Umi's plugin system
*/
interface IApi extends IUmiApi {
/** Dumi-specific configuration */
config: IDumiConfig & { [key: string]: any };
/** User configuration */
userConfig: IDumiUserConfig;
/** Enhanced service with theme and parser data */
service: IUmiApi['service'] & {
themeData: IThemeLoadResult;
atomParser: IAtomAssetsParser;
};
}
/**
* Language metadata parser for API documentation
*/
interface ILanguageMetaParser {
/** Language identifier */
language: string;
/** Parse component/function metadata */
parse(code: string, options: {
filename: string;
entryFile?: string;
}): Promise<AtomAsset[]>;
}
/**
* Options for creating API parsers
*/
interface IBaseApiParserOptions {
/** Entry file for parsing */
entryFile?: string;
/** Custom resolver function */
resolveFilter?: (args: {
id: string;
type: 'COMPONENT' | 'FUNCTION';
}) => boolean;
}Usage:
// Advanced plugin with custom API parser
export default (api: IApi) => {
api.describe({ key: 'custom-plugin' });
// Register custom methods
api.registerMethod({ name: 'addCustomTab' });
api.registerMethod({ name: 'enhanceMetadata' });
// Hook into lifecycle events
api.onStart(() => {
console.log('Plugin starting...');
});
api.onBuildComplete(() => {
console.log('Build completed, running custom tasks...');
});
// Access service data
const { themeData, atomParser } = api.service;
// Custom parsing logic
api.modifyConfig((memo) => {
memo.resolve.entryFile = './src/custom-entry.ts';
return memo;
});
};// Complete design system enhancement plugin
export default (api: IApi) => {
api.describe({ key: 'design-system-plugin' });
// Add design tokens tab
api.addContentTab(() => ({
key: 'tokens',
title: 'Design Tokens',
component: require.resolve('./DesignTokensTab'),
}));
// Enhance theme with design system components
api.modifyTheme((memo) => {
memo.builtins.TokenTable = require.resolve('./TokenTable');
memo.builtins.ColorPalette = require.resolve('./ColorPalette');
memo.builtins.Typography = require.resolve('./Typography');
return memo;
});
// Add design system metadata
api.modifyAssetsMetadata((assets) => {
assets.designSystem = {
tokens: loadDesignTokens(),
guidelines: loadDesignGuidelines(),
};
return assets;
});
};// Plugin supporting React, Vue, and Angular
export default (api: IApi) => {
// Register Vue tech stack
api.registerTechStack(() => new VueTechStack());
// Register Angular tech stack
api.registerTechStack(() => new AngularTechStack());
// Add framework selector tab
api.addContentTab(() => ({
key: 'frameworks',
title: 'Frameworks',
component: require.resolve('./FrameworkSelector'),
}));
};
class VueTechStack extends IDumiTechStack {
name = 'vue';
isSupported(node, lang) {
return lang === 'vue';
}
transformCode(raw, opts) {
return compileVueComponent(raw, opts);
}
}// Plugin for tracking component usage and demos
export default (api: IApi) => {
api.describe({ key: 'analytics-plugin' });
// Track component views
api.modifyTheme((memo) => {
memo.builtins.TrackingPreviewer = require.resolve('./TrackingPreviewer');
return memo;
});
// Generate usage reports
api.onBuildComplete(async () => {
const assets = await api.getAssetsMetadata?.();
generateUsageReport(assets);
});
};Install with Tessl CLI
npx tessl i tessl/npm-dumi