or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration-options.mdevents-hooks.mdindex.mdplugin-bootstrap.mdrouting-system.mdtheme-system.md

events-hooks.mddocs/

0

# Events and Lifecycle Hooks

1

2

Event system for hooking into the markdown generation lifecycle, enabling customization at various rendering stages through page events, renderer events, and hook points.

3

4

## Capabilities

5

6

### MarkdownPageEvent Class

7

8

Event emitted before and after the markdown content of each page is rendered, providing access to page data and content modification.

9

10

```typescript { .api }

11

/**

12

* An event emitted before and after the markdown of a page is rendered.

13

* Provides access to page model, content, and metadata for customization.

14

*/

15

class MarkdownPageEvent<out Model extends RouterTarget = RouterTarget> {

16

/** The project the renderer is currently processing */

17

project: ProjectReflection;

18

19

/** The filename the page will be written to */

20

filename: string;

21

22

/** The URL this page will be located at */

23

url: string;

24

25

/** The type of page this is (index, reflection, document, etc.) */

26

pageKind: PageKind;

27

28

/** The model that should be rendered on this page */

29

readonly model: Model;

30

31

/** The final markdown content of this page */

32

contents: string;

33

34

/** The frontmatter of this page represented as a key-value object */

35

frontmatter?: Record<string, any>;

36

37

/** Page headings for table of contents generation */

38

pageHeadings: PageHeading[];

39

40

/** Page sections for content organization */

41

pageSections: any;

42

43

/** Async jobs to run before page write */

44

preWriteAsyncJobs: Array<(page: MarkdownPageEvent) => Promise<void>>;

45

46

/**

47

* Creates a new page event

48

* @param model - The model to render on this page

49

*/

50

constructor(model: Model);

51

52

/**

53

* Hidden method to start a new section in the page

54

*/

55

startNewSection(): void;

56

57

/**

58

* Type guard to check if this is a reflection event

59

* @returns True if this event is for a reflection page

60

*/

61

isReflectionEvent(): this is MarkdownPageEvent<Reflection>;

62

63

/** Event name triggered before a document will be rendered */

64

static readonly BEGIN = 'beginPage';

65

66

/** Event name triggered after a document has been rendered */

67

static readonly END = 'endPage';

68

}

69

```

70

71

**Usage Example:**

72

73

```typescript

74

import { MarkdownPageEvent } from "typedoc-plugin-markdown";

75

76

// Listen for page events

77

renderer.on(MarkdownPageEvent.BEGIN, (page) => {

78

console.log(`Starting to render: ${page.filename}`);

79

80

// Modify frontmatter before rendering

81

page.frontmatter = {

82

...page.frontmatter,

83

author: 'Documentation Team',

84

lastModified: new Date().toISOString(),

85

pageType: page.pageKind

86

};

87

});

88

89

renderer.on(MarkdownPageEvent.END, (page) => {

90

console.log(`Finished rendering: ${page.filename}`);

91

92

// Post-process content

93

page.contents = page.contents.replace(

94

/<!-- INJECT_TOC -->/g,

95

generateTableOfContents(page.pageHeadings)

96

);

97

98

// Add custom footer

99

page.contents += '\n\n---\n*Generated with TypeDoc Plugin Markdown*';

100

});

101

```

102

103

### MarkdownRendererEvent Class

104

105

Event emitted at the beginning and end of the entire rendering process, providing access to project-wide data and navigation.

106

107

```typescript { .api }

108

/**

109

* An event emitted at the beginning and end of the rendering process.

110

* Provides access to project data, output directory, and page collection.

111

*/

112

class MarkdownRendererEvent {

113

/** The project the renderer is currently processing */

114

readonly project: ProjectReflection;

115

116

/** The path of the directory the documentation should be written to */

117

readonly outputDirectory: string;

118

119

/** A list of all pages that will be generated */

120

pages: PageDefinition[];

121

122

/** The navigation structure of the project */

123

navigation?: NavigationItem[];

124

125

/**

126

* Creates a new renderer event

127

* @param outputDirectory - Directory path for output

128

* @param project - The TypeDoc project being rendered

129

* @param pages - Array of page definitions to generate

130

*/

131

constructor(

132

outputDirectory: string,

133

project: ProjectReflection,

134

pages: PageDefinition[]

135

);

136

137

/** Event name triggered before the renderer starts rendering */

138

static readonly BEGIN = 'beginRender';

139

140

/** Event name triggered after the renderer has written all documents */

141

static readonly END = 'endRender';

142

}

143

```

144

145

**Usage Example:**

146

147

```typescript

148

import { MarkdownRendererEvent } from "typedoc-plugin-markdown";

149

150

// Listen for renderer events

151

renderer.on(MarkdownRendererEvent.BEGIN, (event) => {

152

console.log(`Starting documentation generation for ${event.project.name}`);

153

console.log(`Output directory: ${event.outputDirectory}`);

154

console.log(`Total pages to generate: ${event.pages.length}`);

155

156

// Pre-process pages list

157

event.pages = event.pages.filter(page =>

158

!page.filename.includes('.internal.')

159

);

160

161

// Generate navigation if not present

162

if (!event.navigation) {

163

event.navigation = generateCustomNavigation(event.project);

164

}

165

});

166

167

renderer.on(MarkdownRendererEvent.END, (event) => {

168

console.log(`Documentation generation complete`);

169

170

// Generate index file

171

const indexContent = generateProjectIndex(event.project, event.navigation);

172

writeFileSync(

173

path.join(event.outputDirectory, 'index.md'),

174

indexContent

175

);

176

177

// Generate sitemap

178

generateSitemap(event.pages, event.outputDirectory);

179

});

180

```

181

182

### MarkdownRenderer Interface

183

184

Extended renderer interface with custom hooks and async job support for markdown-specific functionality.

185

186

```typescript { .api }

187

/**

188

* The MarkdownRenderer extends TypeDoc's Renderer with custom hooks and async jobs

189

*/

190

interface MarkdownRenderer extends Renderer {

191

/** Dedicated markdown hooks for injecting content */

192

markdownHooks: EventHooks<MarkdownRendererHooks, string>;

193

194

/** Pre-render async jobs that run before documentation generation */

195

preRenderAsyncJobs: Array<(output: MarkdownRendererEvent) => Promise<void>>;

196

197

/** Post-render async jobs that run after documentation generation */

198

postRenderAsyncJobs: Array<(output: MarkdownRendererEvent) => Promise<void>>;

199

200

/** Store metadata about packages for packages mode */

201

packagesMeta: Record<string, { description: string; options: Options }>;

202

203

/**

204

* Event listener for page events

205

* @param event - Page event type

206

* @param callback - Callback function for page events

207

*/

208

on(

209

event: typeof MarkdownPageEvent.BEGIN | typeof MarkdownPageEvent.END,

210

callback: (page: MarkdownPageEvent) => void

211

): void;

212

213

/**

214

* Event listener for renderer events

215

* @param event - Renderer event type

216

* @param callback - Callback function for renderer events

217

*/

218

on(

219

event: typeof MarkdownRendererEvent.BEGIN | typeof MarkdownRendererEvent.END,

220

callback: (event: MarkdownRendererEvent) => void

221

): void;

222

223

/**

224

* Define a new theme for the renderer

225

* @param name - Theme name

226

* @param theme - Theme constructor class

227

*/

228

defineTheme(name: string, theme: new (renderer: Renderer) => MarkdownTheme): void;

229

}

230

```

231

232

### MarkdownRendererHooks Interface

233

234

Describes the hooks available for injecting content at various points in the markdown rendering process.

235

236

```typescript { .api }

237

/**

238

* Describes the hooks available to inject output in the markdown theme.

239

* Each hook receives a MarkdownThemeContext for accessing page data and utilities.

240

*/

241

interface MarkdownRendererHooks {

242

/** Applied at the start of markdown output */

243

['page.begin']: [MarkdownThemeContext];

244

245

/** Applied at the end of markdown output */

246

['page.end']: [MarkdownThemeContext];

247

248

/** Applied before main markdown content is rendered */

249

['content.begin']: [MarkdownThemeContext];

250

251

/** Applied at start of markdown output on index page only */

252

['index.page.begin']: [MarkdownThemeContext];

253

254

/** Applied at end of markdown output on index page only */

255

['index.page.end']: [MarkdownThemeContext];

256

}

257

```

258

259

**Hook Usage Example:**

260

261

```typescript

262

import { MarkdownThemeContext } from "typedoc-plugin-markdown";

263

264

// Register hooks for content injection

265

renderer.markdownHooks.on('page.begin', (context: MarkdownThemeContext) => {

266

// Add custom header to all pages

267

return `<!-- Generated: ${new Date().toISOString()} -->

268

<!-- Project: ${context.page.project.name} -->

269

270

`;

271

});

272

273

renderer.markdownHooks.on('page.end', (context: MarkdownThemeContext) => {

274

// Add custom footer to all pages

275

const relativeHome = context.relativeURL('/');

276

return `

277

278

---

279

[Back to Home](${relativeHome}) | [View Source](${getSourceUrl(context.page.model)})

280

`;

281

});

282

283

renderer.markdownHooks.on('content.begin', (context: MarkdownThemeContext) => {

284

// Add table of contents before main content

285

if (context.page.pageHeadings?.length > 0) {

286

return generateTOC(context.page.pageHeadings);

287

}

288

return '';

289

});

290

291

renderer.markdownHooks.on('index.page.begin', (context: MarkdownThemeContext) => {

292

// Special header for index page only

293

return `# ${context.page.project.name} Documentation

294

295

Welcome to the API documentation for ${context.page.project.name}.

296

297

`;

298

});

299

```

300

301

### Async Jobs System

302

303

System for registering asynchronous jobs that run before or after the rendering process.

304

305

**Pre-render Jobs:**

306

307

```typescript

308

// Register pre-render job

309

renderer.preRenderAsyncJobs.push(async (event: MarkdownRendererEvent) => {

310

console.log('Pre-processing documentation...');

311

312

// Generate additional metadata

313

const metadata = await analyzeProject(event.project);

314

315

// Store in renderer for use during rendering

316

renderer.packagesMeta[event.project.name] = {

317

description: metadata.description,

318

options: event.project.options

319

};

320

321

// Fetch external data

322

const changelog = await fetchChangelog(event.project);

323

324

// Add changelog page

325

event.pages.push({

326

model: event.project,

327

filename: 'CHANGELOG.md',

328

url: 'changelog.html',

329

contents: changelog

330

});

331

});

332

```

333

334

**Post-render Jobs:**

335

336

```typescript

337

// Register post-render job

338

renderer.postRenderAsyncJobs.push(async (event: MarkdownRendererEvent) => {

339

console.log('Post-processing documentation...');

340

341

// Generate search index

342

const searchIndex = await generateSearchIndex(event.pages);

343

await writeFile(

344

path.join(event.outputDirectory, 'search-index.json'),

345

JSON.stringify(searchIndex)

346

);

347

348

// Optimize images

349

await optimizeImages(event.outputDirectory);

350

351

// Generate RSS feed

352

const feed = generateRSSFeed(event.project, event.pages);

353

await writeFile(

354

path.join(event.outputDirectory, 'feed.xml'),

355

feed

356

);

357

358

console.log('Documentation post-processing complete');

359

});

360

```

361

362

**Complete Event Handling Example:**

363

364

```typescript

365

import {

366

MarkdownPageEvent,

367

MarkdownRendererEvent,

368

MarkdownThemeContext

369

} from "typedoc-plugin-markdown";

370

371

class DocumentationProcessor {

372

setupEventHandlers(renderer: MarkdownRenderer) {

373

// Page-level event handling

374

renderer.on(MarkdownPageEvent.BEGIN, this.handlePageBegin.bind(this));

375

renderer.on(MarkdownPageEvent.END, this.handlePageEnd.bind(this));

376

377

// Renderer-level event handling

378

renderer.on(MarkdownRendererEvent.BEGIN, this.handleRenderBegin.bind(this));

379

renderer.on(MarkdownRendererEvent.END, this.handleRenderEnd.bind(this));

380

381

// Content injection hooks

382

renderer.markdownHooks.on('page.begin', this.injectPageHeader.bind(this));

383

renderer.markdownHooks.on('page.end', this.injectPageFooter.bind(this));

384

renderer.markdownHooks.on('content.begin', this.injectTOC.bind(this));

385

386

// Async jobs

387

renderer.preRenderAsyncJobs.push(this.preRenderSetup.bind(this));

388

renderer.postRenderAsyncJobs.push(this.postRenderCleanup.bind(this));

389

}

390

391

private handlePageBegin(page: MarkdownPageEvent) {

392

console.log(`Rendering page: ${page.filename}`);

393

394

// Set up frontmatter

395

page.frontmatter = {

396

title: this.getPageTitle(page.model),

397

type: page.pageKind,

398

generated: new Date().toISOString()

399

};

400

}

401

402

private handlePageEnd(page: MarkdownPageEvent) {

403

// Validate generated content

404

this.validatePageContent(page);

405

406

// Add analytics tracking

407

page.contents += this.getAnalyticsCode(page);

408

}

409

410

private async preRenderSetup(event: MarkdownRendererEvent) {

411

// Initialize external services

412

await this.initializeServices();

413

414

// Pre-process project data

415

this.processProjectMetadata(event.project);

416

}

417

418

private async postRenderCleanup(event: MarkdownRendererEvent) {

419

// Generate additional files

420

await this.generateSupportingFiles(event);

421

422

// Clean up temporary resources

423

await this.cleanup();

424

}

425

}

426

```