or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

backends.mdcli.mdconfig.mdindex.mdpreprocessors.mdutils.md

backends.mddocs/

0

# Backend System

1

2

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

3

4

## Capabilities

5

6

### Base Backend Class

7

8

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

9

10

```python { .api }

11

class BaseBackend:

12

"""Base backend class that all backends must inherit from."""

13

14

targets: tuple = ()

15

required_preprocessors_before: tuple = ()

16

required_preprocessors_after: tuple = ()

17

18

def __init__(self, context: dict, logger: Logger, quiet=False, debug=False):

19

"""

20

Initialize backend with build context.

21

22

Parameters:

23

- context (dict): Build context containing project_path, config, target, backend

24

- logger (Logger): Logger instance for build messages

25

- quiet (bool): Suppress output messages

26

- debug (bool): Enable debug logging

27

"""

28

29

def get_slug(self) -> str:

30

"""

31

Generate project slug from title, version, and date.

32

Used for output file/directory naming.

33

34

Returns:

35

str: Generated slug (title-version-date format)

36

"""

37

38

def apply_preprocessor(self, preprocessor):

39

"""

40

Apply single preprocessor to working directory content.

41

42

Parameters:

43

- preprocessor (str or dict): Preprocessor name or config dict

44

45

Raises:

46

ModuleNotFoundError: If preprocessor not installed

47

RuntimeError: If preprocessor application fails

48

"""

49

50

def preprocess_and_make(self, target: str) -> str:

51

"""

52

Apply all required preprocessors then generate output.

53

Main entry point for backend execution.

54

55

Parameters:

56

- target (str): Output format (pdf, html, docx, etc.)

57

58

Returns:

59

str: Path to generated output

60

"""

61

62

def make(self, target: str) -> str:

63

"""

64

Generate output from preprocessed source.

65

Must be implemented by each backend.

66

67

Parameters:

68

- target (str): Output format

69

70

Returns:

71

str: Path to generated output

72

73

Raises:

74

NotImplementedError: If not implemented by subclass

75

"""

76

```

77

78

### Preprocessor Backend

79

80

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

81

82

```python { .api }

83

class Backend(BaseBackend):

84

"""

85

Preprocessor-only backend that applies preprocessors

86

and returns processed source without further transformation.

87

"""

88

89

targets = ('pre',)

90

91

def __init__(self, *args, **kwargs):

92

"""

93

Initialize preprocessor backend with configuration.

94

95

Parameters:

96

- *args: Arguments passed to BaseBackend

97

- **kwargs: Keyword arguments passed to BaseBackend

98

"""

99

100

def make(self, target: str) -> str:

101

"""

102

Copy preprocessed content to output directory.

103

Uses slug from backend config or generates default slug.

104

105

Parameters:

106

- target (str): Must be 'pre'

107

108

Returns:

109

str: Path to preprocessed content directory (ends with .pre)

110

"""

111

```

112

113

## Backend Context Structure

114

115

```python { .api }

116

# Context dictionary passed to backends

117

Context = {

118

'project_path': Path, # Path to project directory

119

'config': dict, # Parsed configuration

120

'target': str, # Target format (html, pdf, etc.)

121

'backend': str # Backend name

122

}

123

```

124

125

## Usage Examples

126

127

### Custom Backend Implementation

128

129

```python

130

from foliant.backends.base import BaseBackend

131

from pathlib import Path

132

import subprocess

133

134

class CustomBackend(BaseBackend):

135

"""Custom backend for special output format."""

136

137

targets = ('custom', 'special')

138

required_preprocessors_before = ('includes',)

139

required_preprocessors_after = ('_unescape',)

140

141

def make(self, target: str) -> str:

142

"""Generate custom format output."""

143

output_path = f"{self.get_slug()}.{target}"

144

145

# Custom processing logic

146

self.logger.info(f"Generating {target} format")

147

148

# Example: run external tool

149

subprocess.run([

150

'custom-tool',

151

'--input', str(self.working_dir),

152

'--output', output_path,

153

'--format', target

154

], check=True)

155

156

return output_path

157

```

158

159

### Backend Usage in Build Process

160

161

```python

162

from foliant.backends.base import BaseBackend

163

from foliant.config import Parser

164

from pathlib import Path

165

import logging

166

167

# Set up context

168

project_path = Path('./my-project')

169

config = Parser(project_path, 'foliant.yml', logging.getLogger()).parse()

170

171

context = {

172

'project_path': project_path,

173

'config': config,

174

'target': 'html',

175

'backend': 'mkdocs'

176

}

177

178

# Import and use backend

179

from foliant.backends.mkdocs import Backend

180

181

backend = Backend(

182

context=context,

183

logger=logging.getLogger(),

184

quiet=False,

185

debug=True

186

)

187

188

# Generate output

189

result = backend.preprocess_and_make('html')

190

print(f"Documentation generated at: {result}")

191

```

192

193

### Preprocessor Application

194

195

```python

196

from foliant.backends.base import BaseBackend

197

198

class ExampleBackend(BaseBackend):

199

targets = ('example',)

200

201

def make(self, target: str) -> str:

202

# Apply individual preprocessors

203

self.apply_preprocessor('includes')

204

self.apply_preprocessor({

205

'plantuml': {

206

'format': 'png',

207

'server_url': 'http://localhost:8080'

208

}

209

})

210

211

# Generate output

212

output_file = f"{self.get_slug()}.{target}"

213

# ... backend-specific generation logic

214

return output_file

215

```

216

217

### Backend Discovery and Validation

218

219

```python

220

from foliant.utils import get_available_backends

221

222

# Get all available backends

223

backends = get_available_backends()

224

print("Available backends:")

225

for name, targets in backends.items():

226

print(f" {name}: {', '.join(targets)}")

227

228

# Check if backend supports target

229

def validate_backend(backend_name: str, target: str) -> bool:

230

backends = get_available_backends()

231

if backend_name not in backends:

232

return False

233

return target in backends[backend_name]

234

235

# Usage

236

if validate_backend('pandoc', 'pdf'):

237

print("Pandoc can generate PDF")

238

239

if not validate_backend('mkdocs', 'pdf'):

240

print("MkDocs cannot generate PDF")

241

```

242

243

### Working with Preprocessor Requirements

244

245

```python

246

class DocumentationBackend(BaseBackend):

247

"""Backend with specific preprocessor requirements."""

248

249

targets = ('docs',)

250

# These run before config preprocessors

251

required_preprocessors_before = ('metadata', 'includes')

252

# These run after config preprocessors

253

required_preprocessors_after = ('links', '_unescape')

254

255

def make(self, target: str) -> str:

256

# Preprocessor execution order:

257

# 1. required_preprocessors_before

258

# 2. config['preprocessors']

259

# 3. required_preprocessors_after

260

261

output_dir = Path(f"{self.get_slug()}_docs")

262

# Generate documentation

263

return str(output_dir)

264

```

265

266

## Backend Configuration

267

268

Backends can access configuration through `self.config`:

269

270

```python

271

class ConfigurableBackend(BaseBackend):

272

def make(self, target: str) -> str:

273

# Access backend-specific config

274

backend_config = self.config.get('backend_config', {})

275

my_config = backend_config.get('my_backend', {})

276

277

# Use configuration

278

theme = my_config.get('theme', 'default')

279

custom_css = my_config.get('custom_css', [])

280

281

# Apply configuration in output generation

282

return self.generate_with_config(theme, custom_css)

283

```

284

285

Example `foliant.yml` backend configuration:

286

```yaml

287

title: My Project

288

289

backend_config:

290

my_backend:

291

theme: material

292

custom_css:

293

- assets/style.css

294

- assets/custom.css

295

options:

296

minify: true

297

```