CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-foliant

Modular, Markdown-based documentation generator that makes pdf, docx, html, and more.

Pending
Overview
Eval results
Files

backends.mddocs/

Backend System

Foliant's backend system provides pluggable output generation for multiple document formats. Backends orchestrate preprocessors and handle the final transformation from processed Markdown to the desired output format (PDF, HTML, DOCX, etc.).

Capabilities

Base Backend Class

Foundation class for all output generation backends providing preprocessor orchestration and common functionality.

class BaseBackend:
    """Base backend class that all backends must inherit from."""
    
    targets: tuple = ()
    required_preprocessors_before: tuple = ()
    required_preprocessors_after: tuple = ()
    
    def __init__(self, context: dict, logger: Logger, quiet=False, debug=False):
        """
        Initialize backend with build context.
        
        Parameters:
        - context (dict): Build context containing project_path, config, target, backend
        - logger (Logger): Logger instance for build messages
        - quiet (bool): Suppress output messages
        - debug (bool): Enable debug logging
        """
    
    def get_slug(self) -> str:
        """
        Generate project slug from title, version, and date.
        Used for output file/directory naming.
        
        Returns:
        str: Generated slug (title-version-date format)
        """
    
    def apply_preprocessor(self, preprocessor):
        """
        Apply single preprocessor to working directory content.
        
        Parameters:
        - preprocessor (str or dict): Preprocessor name or config dict
        
        Raises:
        ModuleNotFoundError: If preprocessor not installed
        RuntimeError: If preprocessor application fails
        """
    
    def preprocess_and_make(self, target: str) -> str:
        """
        Apply all required preprocessors then generate output.
        Main entry point for backend execution.
        
        Parameters:
        - target (str): Output format (pdf, html, docx, etc.)
        
        Returns:
        str: Path to generated output
        """
    
    def make(self, target: str) -> str:
        """
        Generate output from preprocessed source.
        Must be implemented by each backend.
        
        Parameters:
        - target (str): Output format
        
        Returns:
        str: Path to generated output
        
        Raises:
        NotImplementedError: If not implemented by subclass
        """

Preprocessor Backend

Built-in backend that applies preprocessors and returns preprocessed source without further transformation.

class Backend(BaseBackend):
    """
    Preprocessor-only backend that applies preprocessors 
    and returns processed source without further transformation.
    """
    
    targets = ('pre',)
    
    def __init__(self, *args, **kwargs):
        """
        Initialize preprocessor backend with configuration.
        
        Parameters:
        - *args: Arguments passed to BaseBackend
        - **kwargs: Keyword arguments passed to BaseBackend
        """
    
    def make(self, target: str) -> str:
        """
        Copy preprocessed content to output directory.
        Uses slug from backend config or generates default slug.
        
        Parameters:
        - target (str): Must be 'pre'
        
        Returns:
        str: Path to preprocessed content directory (ends with .pre)
        """

Backend Context Structure

# Context dictionary passed to backends
Context = {
    'project_path': Path,    # Path to project directory
    'config': dict,          # Parsed configuration
    'target': str,           # Target format (html, pdf, etc.)
    'backend': str           # Backend name
}

Usage Examples

Custom Backend Implementation

from foliant.backends.base import BaseBackend
from pathlib import Path
import subprocess

class CustomBackend(BaseBackend):
    """Custom backend for special output format."""
    
    targets = ('custom', 'special')
    required_preprocessors_before = ('includes',)
    required_preprocessors_after = ('_unescape',)
    
    def make(self, target: str) -> str:
        """Generate custom format output."""
        output_path = f"{self.get_slug()}.{target}"
        
        # Custom processing logic
        self.logger.info(f"Generating {target} format")
        
        # Example: run external tool
        subprocess.run([
            'custom-tool',
            '--input', str(self.working_dir),
            '--output', output_path,
            '--format', target
        ], check=True)
        
        return output_path

Backend Usage in Build Process

from foliant.backends.base import BaseBackend
from foliant.config import Parser
from pathlib import Path
import logging

# Set up context
project_path = Path('./my-project')
config = Parser(project_path, 'foliant.yml', logging.getLogger()).parse()

context = {
    'project_path': project_path,
    'config': config,
    'target': 'html',
    'backend': 'mkdocs'
}

# Import and use backend
from foliant.backends.mkdocs import Backend

backend = Backend(
    context=context,
    logger=logging.getLogger(),
    quiet=False,
    debug=True
)

# Generate output
result = backend.preprocess_and_make('html')
print(f"Documentation generated at: {result}")

Preprocessor Application

from foliant.backends.base import BaseBackend

class ExampleBackend(BaseBackend):
    targets = ('example',)
    
    def make(self, target: str) -> str:
        # Apply individual preprocessors
        self.apply_preprocessor('includes')
        self.apply_preprocessor({
            'plantuml': {
                'format': 'png',
                'server_url': 'http://localhost:8080'
            }
        })
        
        # Generate output
        output_file = f"{self.get_slug()}.{target}"
        # ... backend-specific generation logic
        return output_file

Backend Discovery and Validation

from foliant.utils import get_available_backends

# Get all available backends
backends = get_available_backends()
print("Available backends:")
for name, targets in backends.items():
    print(f"  {name}: {', '.join(targets)}")

# Check if backend supports target
def validate_backend(backend_name: str, target: str) -> bool:
    backends = get_available_backends()
    if backend_name not in backends:
        return False
    return target in backends[backend_name]

# Usage
if validate_backend('pandoc', 'pdf'):
    print("Pandoc can generate PDF")

if not validate_backend('mkdocs', 'pdf'):
    print("MkDocs cannot generate PDF")

Working with Preprocessor Requirements

class DocumentationBackend(BaseBackend):
    """Backend with specific preprocessor requirements."""
    
    targets = ('docs',)
    # These run before config preprocessors
    required_preprocessors_before = ('metadata', 'includes')
    # These run after config preprocessors  
    required_preprocessors_after = ('links', '_unescape')
    
    def make(self, target: str) -> str:
        # Preprocessor execution order:
        # 1. required_preprocessors_before
        # 2. config['preprocessors'] 
        # 3. required_preprocessors_after
        
        output_dir = Path(f"{self.get_slug()}_docs")
        # Generate documentation
        return str(output_dir)

Backend Configuration

Backends can access configuration through self.config:

class ConfigurableBackend(BaseBackend):
    def make(self, target: str) -> str:
        # Access backend-specific config
        backend_config = self.config.get('backend_config', {})
        my_config = backend_config.get('my_backend', {})
        
        # Use configuration
        theme = my_config.get('theme', 'default')
        custom_css = my_config.get('custom_css', [])
        
        # Apply configuration in output generation
        return self.generate_with_config(theme, custom_css)

Example foliant.yml backend configuration:

title: My Project

backend_config:
  my_backend:
    theme: material
    custom_css:
      - assets/style.css
      - assets/custom.css
    options:
      minify: true

Install with Tessl CLI

npx tessl i tessl/pypi-foliant

docs

backends.md

cli.md

config.md

index.md

preprocessors.md

utils.md

tile.json