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);
});
};