Skip to main content

Overview

The generation layer is the central orchestrator of mint-tsdocs. It traverses the API model and constructs the semantic structure of each documentation page using TSDoc AST nodes. The layer has been refactored to use modular components for navigation management and caching.
Primary Component: src/documenters/MarkdownDocumenter.ts
Recent Architecture Changes: The generation layer now delegates navigation to NavigationManager and integrates caching through CacheManager for performance optimization.

Key Responsibilities

File Management

Deletes old output and writes new MDX files

API Traversal

Recursively iterates through packages and API items

AST Construction

Builds TSDoc AST representing document structure

Navigation Management

Delegates to NavigationManager for docs.json updates

Caching Integration

Coordinates with CacheManager for optimization

Core Generation Flow

1

Delete Old Output

_deleteOldOutputFiles() cleans the output directory
This step permanently deletes all files in the output folder!
2

Traverse API Model

Recursively iterate through the API hierarchy:
  • Packages
  • Namespaces
  • Classes, Interfaces, Types
  • Functions, Variables
Member-level items (methods, properties) are documented within their parent’s page, not as separate files.
3

Generate Each Page

For each API item, call _writeApiItemPage():
  1. Build frontmatter (YAML)
  2. Construct TSDoc AST
  3. Pass to emitter
  4. Write MDX file
  5. Add to navigation list
4

Update Navigation

NavigationManager.generateNavigation() merges new pages into docs.json
5

Cache Statistics

CacheManager.printStats() reports cache hit rates and performance

Modular Architecture

Architecture Evolution: The generation layer has been refactored to separate concerns and improve maintainability.
Performance Optimization: Coordinates with caching layer for improved performance:
    // Initialize cache manager
    const cacheManager = getGlobalCacheManager({
    enabled: true,
    enableStats: true,
    typeAnalysis: { maxSize: 1000, enabled: true },
    apiResolution: { maxSize: 500, enabled: true }
    });

    // Cache statistics reported after generation
    cacheManager.printStats();
Performance Improvements:
  • 30-50% faster type analysis
  • 20-40% faster API resolution
  • Significant speedup for large codebases
Built-in Cache Statistics: Track cache effectiveness for optimization:
    // Check cache performance
    const cacheStats = cacheManager.getStats();
    cacheManager.printStats();
Metrics Tracked:
  • Cache hit rates
  • Type analysis cache performance
  • API resolution cache performance

AST Construction (Not Markdown!)

Key Insight: The documenter builds a TSDoc AST (Abstract Syntax Tree), not Markdown strings. This separates content structure from presentation format.

Why Use an AST?

  • What a document contains (generation layer)
  • How it looks (emission layer)
Clear separation makes both easier to maintain.
To change MDX output for a specific element:
  • Modify only the emitter’s handling for that node type
  • No need to touch document generation logic
Can define custom DocNode types for unique concepts:
  • DocTable for tabular data
  • DocHeading for section titles
  • DocExpandable for Mintlify expandables

Page Generation Example

Let’s trace how a class page is generated:
  • 1. Entry Point
  • 2. Create Page Structure
  • 3. Build Members Table
  • 4. Generate Frontmatter
  • 5. Emit to MDX
// User's API includes:
export class DatabaseClient {
  constructor(config: Config) { }
  async connect(): Promise<void> { }
  async query(sql: string): Promise<Results> { }
}
MarkdownDocumenter receives this as an ApiClass item.

Key Methods

The Main Page GeneratorResponsibilities:
  • Determine page type (class, interface, function, etc.)
  • Build appropriate AST structure
  • Generate frontmatter
  • Invoke emitter
  • Write file
  • Add to navigation
private _writeApiItemPage(apiItem: ApiItem): void {
  const output = new DocSection({ configuration });

  // Build AST based on item kind
  switch (apiItem.kind) {
    case ApiItemKind.Class:
      this._writeClassPage(output, apiItem as ApiClass);
      break;
    case ApiItemKind.Interface:
      this._writeInterfacePage(output, apiItem as ApiInterface);
      break;
    // ... other kinds
  }

  // Generate and write file
  const filename = this._getFilenameForApiItem(apiItem);
  const mdx = this._emitApiItem(output, apiItem);
  FileSystem.writeFile(filename, mdx);

  // Track for navigation
  this._navigationItems.push({
    page: relativePath,
    apiKind: apiItem.kind
  });
}
Class Members DocumentationCreates three tables:
  1. Constructors - Class constructors
  2. Properties - Class properties
  3. Methods - Class methods
Each table is a DocTable with:
  • Header row defining columns
  • Data rows for each member
  • Cells containing name, modifiers, type, description
The emitter later decides whether to render as HTML <table> or Mintlify components.
Interface Members DocumentationSimilar to class tables, but for interfaces:
  • Properties table
  • Methods table
  • Call signatures table (if any)
Includes logic to handle:
  • Optional properties (prop?:)
  • Readonly modifiers
  • Generic type parameters
Function ParametersFor functions and methods:
  • Creates parameter table
  • Shows name, type, description
  • Indicates optional vs required
  • Links to type definitions
This table is converted by the emitter into Mintlify <ParamField> components.
Navigation Updates (Now Delegated to NavigationManager)After all files are generated, navigation is handled by the dedicated NavigationManager:
1

Delegate to NavigationManager

Pass navigation generation to specialized component
2

Hierarchical Organization

NavigationManager categorizes pages by API item type
3

docs.json Integration

Supports both simple and Mintlify v4 tab structures
4

Error Handling

Comprehensive error handling with detailed context
public generateNavigation(): void {
  try {
    this._navigationManager.generateNavigation();
  } catch (error) {
    console.error('❌ Failed to generate navigation:', error);
    throw error;
  }
}
Benefits of Delegation:
  • Separation of concerns
  • Specialized navigation logic
  • Mintlify v4 compatibility
  • Better error handling
  • Hierarchical categorization

Frontmatter Generation

Each MDX file starts with YAML frontmatter:
---
title: "ClassName class"
icon: "box"
description: "Brief description from TSDoc comment"
---
function getIcon(apiItem: ApiItem): string {
  switch (apiItem.kind) {
    case ApiItemKind.Class: return 'box';
    case ApiItemKind.Interface: return 'square-i';
    case ApiItemKind.Function: return 'function';
    case ApiItemKind.TypeAlias: return 'type';
    case ApiItemKind.Variable: return 'variable';
    default: return 'file';
  }
}

File Organization

Generated files follow a consistent naming pattern:
API ItemGenerated File
Package my-packagemy-package.mdx
Class MyClassmy-package.myclass.mdx
Interface IConfigmy-package.iconfig.mdx
Function helper()my-package.helper.mdx
Type Statusmy-package.status.mdx
Lowercased filenames with package prefix prevent naming collisions

Performance Considerations

Significant Performance Improvements: The new caching layer provides substantial performance gains for large codebases.
Type Analysis Cache: 30-50% performance improvement
  • Caches expensive TypeScript type parsing operations
  • LRU eviction with configurable size (default: 1000 items)
  • Automatic integration with ObjectTypeAnalyzer
API Resolution Cache: 20-40% performance improvement
  • Caches API model cross-reference resolution
  • Reduces redundant symbol lookups
  • Configurable size (default: 500 items)
Cache Statistics: Monitor effectiveness with built-in reporting
📊 Cache Statistics:
   Overall Hit Rate: 42.3%
   Type Analysis Cache: 45.7% hit rate (457/1000)
   API Resolution Cache: 38.9% hit rate (194/500)
Built-in Performance Tracking:
  • Operation-level execution time measurement
  • Detailed statistics for optimization
  • Error-aware timing (includes failed operations)
Example Output:
📊 Performance Summary:
   Documentation Generation:
     Count: 1
     Total: 5234.56ms
     Average: 5234.56ms
   Type Analysis:
     Count: 150
     Total: 2345.67ms
     Average: 15.64ms
     Range: 2.34ms - 145.23ms
Future Enhancement: Currently, all files are regenerated each time. Potential optimizations:
  • Track file timestamps and API changes
  • Only regenerate changed API items
  • Cache unchanged AST structures
  • Implement dependency tracking
Current Optimizations:
  • Sequential processing to control memory usage
  • LRU cache eviction to limit memory growth
  • Efficient AST construction
For Very Large APIs:
  • Monitor cache hit rates vs. memory usage
  • Adjust cache sizes based on available memory
  • Consider streaming approaches for massive projects
Potential Future Optimization:
  • Generate pages in parallel (currently sequential)
  • Use worker threads for large packages
  • Batch file writes for I/O optimization
  • Thread-safe cache implementations

Extending the Generator

Adding a New Section

To add a new section to all class pages:
function _writeClassPage(output: DocSection, apiClass: ApiClass) {
  // ... existing sections

  // New section: Examples
  if (apiClass.tsdocComment?.customBlocks) {
    const examplesBlock = apiClass.tsdocComment.customBlocks
      .find(b => b.blockTag.tagName === '@example');

    if (examplesBlock) {
      output.appendNode(new DocHeading({
        title: 'Examples',
        level: 2
      }));
      output.appendNode(examplesBlock.content);
    }
  }
}

Supporting a New API Item Kind

case ApiItemKind.Enum:
  this._writeEnumPage(output, apiItem as ApiEnum);
  break;

private _writeEnumPage(output: DocSection, apiEnum: ApiEnum): void {
  // Build table of enum members
  const table = new DocTable({
    header: new DocTableRow({
      cells: [
        new DocTableCell({ content: 'Member' }),
        new DocTableCell({ content: 'Value' }),
        new DocTableCell({ content: 'Description' })
      ]
    })
  });

  for (const member of apiEnum.members) {
    table.addRow(/* ... */);
  }

  output.appendNode(table);
}