CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-dumi

Documentation generator for React component libraries with live demos, API tables, and theming support

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

utilities.mddocs/

Utilities

File system utilities, caching, and build helpers for processing documentation files and managing project structure.

Capabilities

File System Utilities

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() { ... }"

Project Structure Utilities

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

Cache Management

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';

Content Processing

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: false

Webpack Loader Integration

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

Advanced Utility Patterns

Custom File Processing Pipeline

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

Project Structure Analysis

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

Tech Stack Utilities

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

Performance Optimization Helpers

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

Install with Tessl CLI

npx tessl i tessl/npm-dumi

docs

cli.md

configuration.md

index.md

plugins.md

theme-api.md

utilities.md

tile.json