File system utilities, caching, and build helpers for processing documentation files and managing project structure.
Utilities for working with file paths and content extraction.
/**
* Convert filesystem path to route ID using kebab-case
* @param fsPath - Filesystem path to convert
* @returns Kebab-cased route identifier
*/
function getFileIdFromFsPath(fsPath: string): string;
/**
* Extract specific line ranges from file content
* @param content - File content as string
* @param range - Range specification (e.g., "L1-L10", "L5")
* @returns Extracted lines as string
*/
function getFileRangeLines(content: string, range: string): string;
/**
* Extract content from file using regular expression
* @param content - Source file content
* @param regexp - Regular expression string
* @param filePath - Source file path for error reporting
* @returns Matched content or full content on error
*/
function getFileContentByRegExp(
content: string,
regexp: string,
filePath: string
): string;
/**
* Parse frontmatter from code blocks (supports JS/HTML comments)
* @param raw - Raw code string with potential frontmatter
* @returns Object with separated code and frontmatter
*/
function parseCodeFrontmatter(raw: string): {
code: string;
frontmatter: Record<string, any> | null;
};Usage:
import {
getFileIdFromFsPath,
getFileRangeLines,
parseCodeFrontmatter
} from "dumi";
// Convert file paths to route IDs
const routeId = getFileIdFromFsPath('/src/components/ButtonGroup/index.tsx');
// Result: "button-group"
// Extract specific lines from content
const content = `line 1
line 2
line 3
line 4`;
const excerpt = getFileRangeLines(content, 'L2-L3');
// Result: "line 2\nline 3"
// Parse frontmatter from code
const codeWithMeta = `/**
* title: Button Demo
* description: Basic button component
*/
export default function Button() {
return <button>Click me</button>;
}`;
const { code, frontmatter } = parseCodeFrontmatter(codeWithMeta);
// frontmatter: { title: "Button Demo", description: "Basic button component" }
// code: "export default function Button() { ... }"Utilities for working with monorepo projects and build configurations.
/**
* Get root directory for monorepo projects
* @param cwd - Current working directory
* @returns Root directory path for monorepo or current directory
*/
function getProjectRoot(cwd: string): string;
/**
* Attempt to load Father build configurations
* @param cwd - Project directory
* @returns Array of Father build configurations
*/
function tryFatherBuildConfigs(cwd: string): Promise<any[]>;
/**
* Convert component path to webpack chunk name
* @param component - Component file path
* @param cwdPath - Current working directory path
* @returns Webpack-compatible chunk name
*/
function componentToChunkName(
component: string,
cwdPath?: string
): string;
/**
* Generate metadata chunk name for route
* @param path - Route path
* @param cwd - Current working directory
* @param locales - Available locales
* @returns Metadata chunk name
*/
function generateMetaChunkName(
path: string,
cwd: string,
locales?: string[]
): string;Usage:
import {
getProjectRoot,
componentToChunkName,
tryFatherBuildConfigs
} from "dumi";
// Find monorepo root
const projectRoot = getProjectRoot('/path/to/packages/ui-lib');
// Returns: "/path/to" (if workspace detected)
// Generate chunk names
const chunkName = componentToChunkName(
'/src/components/Button/index.tsx',
'/project/root'
);
// Result: "components__Button__index"
// Load Father configs for build integration
const buildConfigs = await tryFatherBuildConfigs(process.cwd());
console.log('Available build configurations:', buildConfigs);File system caching utilities for performance optimization.
/**
* Get filesystem cache instance for specific namespace
* @param ns - Cache namespace
* @returns Cache instance with get/set methods
*/
function getCache(ns: string): {
/** Set cache value */
set(key: string, value: any): Promise<void>;
/** Get cache value */
get(key: string): Promise<any>;
/** Set cache value synchronously */
setSync(key: string, value: any): void;
/** Get cache value synchronously */
getSync(key: string): any;
};
/**
* Set custom cache directory (internal use)
* @param dir - Cache directory path
*/
function _setFSCacheDir(dir: string): void;Usage:
import { getCache } from "dumi";
// Create namespace-specific cache
const apiCache = getCache('api-parsing');
const themeCache = getCache('theme-data');
// Cache expensive operations
async function parseComponentAPI(filePath: string) {
const cacheKey = `api-${getContentHash(filePath)}`;
// Try cache first
let apiData = await apiCache.get(cacheKey);
if (!apiData) {
// Expensive parsing operation
apiData = await parseTypeScriptFile(filePath);
// Cache result
await apiCache.set(cacheKey, apiData);
}
return apiData;
}
// Disable caching for development
process.env.DUMI_CACHE = 'none';Utilities for content hashing and processing.
/**
* Generate MD5 hash for content
* @param content - Content to hash
* @param length - Hash length (default: 8)
* @returns Hex hash string
*/
function getContentHash(content: string, length?: number): string;
/**
* Check if version satisfies range requirement
* @param version - Version string to check
* @param range - Semver range requirement
* @param options - Additional semver options
* @returns Whether version satisfies range
*/
function isVersionInRange(
version: string,
range: string | Range,
options?: RangeOptions
): boolean;Usage:
import { getContentHash, isVersionInRange } from "dumi";
// Generate content hashes for cache keys
const fileContent = 'export default function Component() {}';
const hash = getContentHash(fileContent);
// Result: "a1b2c3d4"
const longHash = getContentHash(fileContent, 16);
// Result: "a1b2c3d4e5f6g7h8"
// Check version compatibility
const isCompatible = isVersionInRange('2.4.21', '^2.0.0');
// Result: true
const supportsFeature = isVersionInRange('1.5.0', '>=1.6.0');
// Result: falseUtilities for webpack loader processing and integration.
/**
* Run webpack loaders programmatically
* @param options - Loader configuration options
* @returns Promise resolving to loader result
*/
function runLoaders(options: RunLoaderOption): Promise<RunLoaderResult>;
interface RunLoaderOption {
/** Resource path to process */
resource?: string;
/** Array of loaders to apply */
loaders?: Array<{
loader: string;
options?: any;
}>;
/** Loader context */
context?: any;
/** Custom resource reader function */
readResource?: (resource: string) => Promise<Buffer>;
}
interface RunLoaderResult {
/** Loader processing results */
result: Buffer[];
/** Original resource buffer */
resourceBuffer: Buffer;
/** Whether result is cacheable */
cacheable: boolean;
/** File dependencies */
fileDependencies: string[];
/** Context dependencies */
contextDependencies: string[];
}
/**
* Get loader context (internal webpack utility)
* @param loader - Loader function or object
* @returns Loader context
*/
function getContext(loader: any): any;Usage:
import { runLoaders } from "dumi";
// Process markdown files with custom loader
async function processMarkdownFile(filePath: string) {
const result = await runLoaders({
resource: filePath,
loaders: [
{
loader: require.resolve('./markdown-loader'),
options: {
extractToc: true,
generateDemos: true,
},
},
],
context: {
rootDir: process.cwd(),
},
});
return {
content: result.result[0].toString(),
dependencies: result.fileDependencies,
cacheable: result.cacheable,
};
}
// Custom loader implementation
function markdownLoader(content: string) {
const options = this.getOptions();
// Process markdown content
const processed = processMarkdown(content, options);
// Track dependencies
this.addDependency('./components/Button.tsx');
return processed;
}import {
parseCodeFrontmatter,
getContentHash,
getCache,
runLoaders
} from "dumi";
class DocumentProcessor {
private cache = getCache('doc-processing');
async processFile(filePath: string, content: string) {
const hash = getContentHash(content);
const cacheKey = `processed-${filePath}-${hash}`;
// Check cache
let result = await this.cache.get(cacheKey);
if (!result) {
// Parse frontmatter
const { code, frontmatter } = parseCodeFrontmatter(content);
// Process with loaders
const loaderResult = await runLoaders({
resource: filePath,
loaders: [
{ loader: require.resolve('./custom-loader') },
],
});
result = {
content: code,
meta: frontmatter,
processed: loaderResult.result[0].toString(),
dependencies: loaderResult.fileDependencies,
};
// Cache result
await this.cache.set(cacheKey, result);
}
return result;
}
}import {
getProjectRoot,
getFileIdFromFsPath,
tryFatherBuildConfigs
} from "dumi";
import path from 'path';
import fs from 'fs';
class ProjectAnalyzer {
async analyzeProject(startDir: string) {
const projectRoot = getProjectRoot(startDir);
const buildConfigs = await tryFatherBuildConfigs(projectRoot);
// Scan for component files
const components = this.scanComponents(projectRoot);
// Generate route mapping
const routes = components.map(filePath => ({
filePath,
routeId: getFileIdFromFsPath(
path.relative(projectRoot, filePath)
),
chunkName: componentToChunkName(filePath, projectRoot),
}));
return {
projectRoot,
buildConfigs,
components: routes,
};
}
private scanComponents(rootDir: string): string[] {
// Implementation for scanning component files
const componentFiles: string[] = [];
function scanDir(dir: string) {
const entries = fs.readdirSync(dir);
for (const entry of entries) {
const fullPath = path.join(dir, entry);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
scanDir(fullPath);
} else if (/\.(tsx?|jsx?)$/.test(entry)) {
componentFiles.push(fullPath);
}
}
}
scanDir(path.join(rootDir, 'src'));
return componentFiles;
}
}Utilities for building custom tech stack integrations and API parsing.
/**
* Language metadata parser interface for tech stacks
*/
interface ILanguageMetaParser {
/** Language identifier */
language: string;
/** Parse component/function metadata from code */
parse(code: string, options: {
filename: string;
entryFile?: string;
}): Promise<AtomAsset[]>;
}
/**
* File patching interface for tech stack processing
*/
interface IPatchFile {
/** File path to patch */
path: string;
/** Patch content */
content: string;
/** Patch type */
type: 'add' | 'modify' | 'delete';
}
/**
* Base options for API parser creation
*/
interface IBaseApiParserOptions {
/** Entry file for parsing */
entryFile?: string;
/** Custom resolver filter function */
resolveFilter?: (args: {
id: string;
ids: string;
type: 'COMPONENT' | 'FUNCTION';
}) => boolean;
}
/**
* Create API parser for tech stack integration
* @param options - Parser configuration options
* @returns API parser instance
*/
function createApiParser(options: IBaseApiParserOptions): ILanguageMetaParser;
/**
* Get Babel core instance for code transformation
* @returns Babel core module
*/
function babelCore(): typeof import('@babel/core');
/**
* Get Babel TypeScript preset
* @returns Babel TypeScript preset configuration
*/
function babelPresetTypeScript(): any;
/**
* Get Babel environment preset
* @returns Babel environment preset configuration
*/
function babelPresetEnv(): any;Usage:
import {
createApiParser,
babelCore,
babelPresetTypeScript,
type ILanguageMetaParser
} from "dumi/tech-stack-utils";
// Create custom API parser
const parser = createApiParser({
entryFile: './src/index.ts',
resolveFilter: ({ id, type }) => {
// Skip private components
return !id.startsWith('_');
},
});
// Use Babel for code transformation
const babel = babelCore();
const result = babel.transformSync(code, {
presets: [babelPresetTypeScript()],
});
// Custom language parser implementation
class VueMetaParser implements ILanguageMetaParser {
language = 'vue';
async parse(code: string, options: { filename: string }) {
// Parse Vue SFC for component metadata
const component = parseVueComponent(code);
return [{
type: 'COMPONENT',
id: getComponentName(options.filename),
props: extractProps(component),
methods: extractMethods(component),
}];
}
}import { getCache, getContentHash } from "dumi";
class PerformanceOptimizer {
private parseCache = getCache('parsing');
private buildCache = getCache('build');
async optimizedParse(filePath: string, parser: Function) {
const content = fs.readFileSync(filePath, 'utf-8');
const hash = getContentHash(content);
const cacheKey = `parse-${filePath}-${hash}`;
let result = await this.parseCache.get(cacheKey);
if (!result) {
console.time(`Parsing ${filePath}`);
result = await parser(content, filePath);
console.timeEnd(`Parsing ${filePath}`);
await this.parseCache.set(cacheKey, result);
}
return result;
}
async batchProcess<T>(
items: T[],
processor: (item: T) => Promise<any>,
concurrency = 5
) {
const results = [];
for (let i = 0; i < items.length; i += concurrency) {
const batch = items.slice(i, i + concurrency);
const batchResults = await Promise.all(
batch.map(processor)
);
results.push(...batchResults);
}
return results;
}
}