or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdconfiguration.mdenvironment.mdindex.mdparser.mdplugins.mdtemplates.mdutilities.md
tile.json

plugins.mddocs/

0

# Plugin System

1

2

JSDoc's plugin system provides extensive customization through event handlers, custom JSDoc tags, and AST node visitors. It includes built-in plugins for common tasks and supports custom plugin development.

3

4

## Capabilities

5

6

### Plugin Installation

7

8

Functions for loading and installing plugins into the JSDoc parser.

9

10

```javascript { .api }

11

/**

12

* Install an array of plugins into a parser instance

13

* @param plugins - Array of plugin module paths

14

* @param parser - Parser instance to install plugins into

15

*/

16

function installPlugins(plugins: string[], parser: Parser): void;

17

```

18

19

### Plugin Interface

20

21

Standard interface that all JSDoc plugins must implement.

22

23

```javascript { .api }

24

interface Plugin {

25

/**

26

* Event handlers for parser events

27

* Maps event names to handler functions

28

*/

29

handlers?: {

30

[eventName: string]: (e: any) => void;

31

};

32

33

/**

34

* Define custom JSDoc tags

35

* @param dictionary - Tag dictionary to add definitions to

36

*/

37

defineTags?(dictionary: TagDictionary): void;

38

39

/**

40

* AST node visitor function

41

* Called for each AST node during parsing

42

*/

43

astNodeVisitor?: (

44

node: ASTNode,

45

e: any,

46

parser: Parser,

47

currentSourceName: string

48

) => void;

49

}

50

```

51

52

## Built-in Plugins

53

54

### Markdown Plugin

55

56

Processes Markdown syntax in JSDoc comments and converts to HTML.

57

58

```javascript

59

// Plugin: plugins/markdown.js

60

const plugin = {

61

handlers: {

62

newDoclet: function(e) {

63

// Process Markdown in description

64

if (e.doclet.description) {

65

e.doclet.description = markdown.parse(e.doclet.description);

66

}

67

}

68

}

69

};

70

```

71

72

**Usage in configuration:**

73

```json

74

{

75

"plugins": ["plugins/markdown"]

76

}

77

```

78

79

### Comment Convert Plugin

80

81

Converts certain comment types to JSDoc comments.

82

83

```javascript

84

// Plugin: plugins/commentConvert.js

85

const plugin = {

86

handlers: {

87

beforeParse: function(e) {

88

// Convert // comments to /** */ format in certain cases

89

e.source = convertComments(e.source);

90

}

91

}

92

};

93

```

94

95

### Escape HTML Plugin

96

97

Escapes HTML content in JSDoc comments to prevent XSS.

98

99

```javascript

100

// Plugin: plugins/escapeHtml.js

101

const plugin = {

102

handlers: {

103

newDoclet: function(e) {

104

if (e.doclet.description) {

105

e.doclet.description = escapeHtml(e.doclet.description);

106

}

107

}

108

}

109

};

110

```

111

112

### Overload Helper Plugin

113

114

Helps document function overloads by grouping related doclets.

115

116

```javascript

117

// Plugin: plugins/overloadHelper.js

118

const plugin = {

119

handlers: {

120

parseComplete: function(e) {

121

// Group overloaded functions

122

groupOverloads(e.doclets);

123

}

124

}

125

};

126

```

127

128

### Source Tag Plugin

129

130

Adds source code information to doclets.

131

132

```javascript

133

// Plugin: plugins/sourcetag.js

134

const plugin = {

135

defineTags: function(dictionary) {

136

dictionary.defineTag('source', {

137

onTagged: function(doclet, tag) {

138

doclet.source = tag.value;

139

}

140

});

141

}

142

};

143

```

144

145

### Summarize Plugin

146

147

Creates summary descriptions from the first sentence of descriptions.

148

149

```javascript

150

// Plugin: plugins/summarize.js

151

const plugin = {

152

handlers: {

153

newDoclet: function(e) {

154

if (e.doclet.description && !e.doclet.summary) {

155

e.doclet.summary = extractFirstSentence(e.doclet.description);

156

}

157

}

158

}

159

};

160

```

161

162

### Event Dumper Plugin

163

164

Debug plugin that logs all parser events to console.

165

166

```javascript

167

// Plugin: plugins/eventDumper.js

168

const plugin = {

169

handlers: {

170

parseBegin: (e) => console.log('parseBegin:', e),

171

fileBegin: (e) => console.log('fileBegin:', e),

172

jsdocCommentFound: (e) => console.log('jsdocCommentFound:', e),

173

newDoclet: (e) => console.log('newDoclet:', e),

174

parseComplete: (e) => console.log('parseComplete:', e)

175

}

176

};

177

```

178

179

### Escape HTML Plugin

180

181

Escapes HTML content in JSDoc comments to prevent XSS attacks.

182

183

```javascript

184

// Plugin: plugins/escapeHtml.js

185

const plugin = {

186

handlers: {

187

newDoclet: function(e) {

188

if (e.doclet.description) {

189

e.doclet.description = escapeHtml(e.doclet.description);

190

}

191

if (e.doclet.summary) {

192

e.doclet.summary = escapeHtml(e.doclet.summary);

193

}

194

}

195

}

196

};

197

```

198

199

### Comments Only Plugin

200

201

Processes only comment content, filtering out code elements.

202

203

```javascript

204

// Plugin: plugins/commentsOnly.js

205

const plugin = {

206

handlers: {

207

beforeParse: function(e) {

208

// Filter source to include only comments

209

e.source = extractCommentsOnly(e.source);

210

}

211

}

212

};

213

```

214

215

### Template Helper Plugins

216

217

Plugins that enhance template functionality:

218

219

#### Rails Template Plugin

220

Rails-style template syntax support for JSDoc templates.

221

222

```javascript

223

// Plugin: plugins/railsTemplate.js

224

const plugin = {

225

handlers: {

226

parseBegin: function(e) {

227

// Configure Rails-style template syntax

228

configureRailsTemplates();

229

}

230

}

231

};

232

```

233

234

#### Underscore Template Plugin

235

Underscore.js template integration for JSDoc.

236

237

```javascript

238

// Plugin: plugins/underscore.js

239

const plugin = {

240

handlers: {

241

parseBegin: function(e) {

242

// Set up Underscore.js template engine

243

configureUnderscoreTemplates();

244

}

245

}

246

};

247

```

248

249

#### Partial Template Plugin

250

Partial template support for modular template organization.

251

252

```javascript

253

// Plugin: plugins/partial.js

254

const plugin = {

255

handlers: {

256

processingComplete: function(e) {

257

// Process partial templates

258

processPartialTemplates(e.doclets);

259

}

260

}

261

};

262

```

263

264

### Text Processing Plugins

265

266

Plugins for text manipulation and formatting:

267

268

#### Shout Plugin

269

Converts specific text elements to uppercase for emphasis.

270

271

```javascript

272

// Plugin: plugins/shout.js

273

const plugin = {

274

handlers: {

275

newDoclet: function(e) {

276

if (e.doclet.description) {

277

e.doclet.description = processShoutText(e.doclet.description);

278

}

279

}

280

}

281

};

282

```

283

284

## Custom Plugin Development

285

286

### Basic Plugin Structure

287

288

```javascript

289

// my-custom-plugin.js

290

exports.handlers = {

291

newDoclet: function(e) {

292

if (e.doclet.kind === 'function') {

293

// Custom processing for functions

294

e.doclet.customProperty = 'processed';

295

}

296

}

297

};

298

299

exports.defineTags = function(dictionary) {

300

dictionary.defineTag('custom', {

301

mustHaveValue: true,

302

onTagged: function(doclet, tag) {

303

doclet.customTag = tag.value;

304

}

305

});

306

};

307

```

308

309

### Event Handler Plugin

310

311

```javascript

312

// event-logger-plugin.js

313

const fs = require('fs');

314

315

exports.handlers = {

316

parseBegin: function(e) {

317

console.log('Starting parse of:', e.sourcefiles.length, 'files');

318

},

319

320

newDoclet: function(e) {

321

if (e.doclet.kind === 'function' && !e.doclet.description) {

322

console.warn(`Undocumented function: ${e.doclet.longname}`);

323

}

324

},

325

326

parseComplete: function(e) {

327

const stats = {

328

total: e.doclets.length,

329

documented: e.doclets.filter(d => !d.undocumented).length,

330

functions: e.doclets.filter(d => d.kind === 'function').length,

331

classes: e.doclets.filter(d => d.kind === 'class').length

332

};

333

334

fs.writeFileSync('doc-stats.json', JSON.stringify(stats, null, 2));

335

}

336

};

337

```

338

339

### Custom Tag Plugin

340

341

```javascript

342

// version-tag-plugin.js

343

exports.defineTags = function(dictionary) {

344

dictionary.defineTag('version', {

345

mustHaveValue: true,

346

canHaveType: false,

347

canHaveName: false,

348

onTagged: function(doclet, tag) {

349

doclet.version = tag.value;

350

}

351

});

352

353

dictionary.defineTag('since', {

354

mustHaveValue: true,

355

onTagged: function(doclet, tag) {

356

doclet.since = tag.value;

357

}

358

});

359

};

360

```

361

362

### AST Node Visitor Plugin

363

364

```javascript

365

// ast-visitor-plugin.js

366

exports.astNodeVisitor = function(node, e, parser, currentSourceName) {

367

// Process specific node types

368

if (node.type === 'CallExpression') {

369

// Track function calls

370

if (node.callee && node.callee.name === 'deprecated') {

371

// Mark enclosing function as deprecated

372

const doclet = parser._getDocletById(node.parent.nodeId);

373

if (doclet) {

374

doclet.deprecated = true;

375

}

376

}

377

}

378

379

if (node.type === 'ClassDeclaration') {

380

// Process class declarations

381

console.log(`Found class: ${node.id ? node.id.name : 'anonymous'}`);

382

}

383

};

384

```

385

386

### Advanced Plugin with Configuration

387

388

```javascript

389

// configurable-plugin.js

390

let pluginConfig = {

391

logLevel: 'info',

392

outputFile: 'plugin-output.json',

393

processPrivate: false

394

};

395

396

exports.handlers = {

397

parseBegin: function(e) {

398

// Load configuration from JSDoc config

399

const jsdocConfig = require('jsdoc/env').conf;

400

if (jsdocConfig.plugins && jsdocConfig.plugins.configurablePlugin) {

401

pluginConfig = { ...pluginConfig, ...jsdocConfig.plugins.configurablePlugin };

402

}

403

},

404

405

newDoclet: function(e) {

406

if (!pluginConfig.processPrivate && e.doclet.access === 'private') {

407

return;

408

}

409

410

if (pluginConfig.logLevel === 'debug') {

411

console.log('Processing doclet:', e.doclet.longname);

412

}

413

}

414

};

415

```

416

417

## Plugin Configuration

418

419

### In JSDoc Configuration File

420

421

```json

422

{

423

"plugins": [

424

"plugins/markdown",

425

"plugins/overloadHelper",

426

"./custom-plugins/my-plugin"

427

],

428

"custom-plugin-config": {

429

"logLevel": "debug",

430

"outputFile": "custom-output.json"

431

}

432

}

433

```

434

435

### Plugin Loading Order

436

437

Plugins are loaded in the order specified in the configuration:

438

439

1. Plugin handlers are registered with the parser

440

2. Custom tags are defined in the tag dictionary

441

3. AST node visitors are added to the parser

442

4. Parser events fire handlers in registration order

443

444

## Plugin Event Reference

445

446

### Available Events

447

448

```javascript { .api }

449

// Parser lifecycle events

450

interface ParserEvents {

451

parseBegin: { sourcefiles: string[] };

452

fileBegin: { filename: string };

453

beforeParse: { filename: string; source: string };

454

jsdocCommentFound: {

455

comment: string;

456

lineno: number;

457

filename: string;

458

};

459

symbolFound: any;

460

newDoclet: { doclet: Doclet };

461

fileComplete: { filename: string };

462

parseComplete: {

463

sourcefiles: string[];

464

doclets: Doclet[];

465

};

466

processingComplete: { doclets: Doclet[] };

467

}

468

```

469

470

### Event Handler Examples

471

472

```javascript

473

// Comprehensive event handling

474

exports.handlers = {

475

parseBegin: function(e) {

476

this.startTime = Date.now();

477

console.log(`Starting parse of ${e.sourcefiles.length} files`);

478

},

479

480

fileBegin: function(e) {

481

console.log(`Processing file: ${e.filename}`);

482

},

483

484

jsdocCommentFound: function(e) {

485

// Validate comment format

486

if (!e.comment.includes('@')) {

487

console.warn(`Minimal JSDoc at ${e.filename}:${e.lineno}`);

488

}

489

},

490

491

newDoclet: function(e) {

492

// Enhance doclets

493

if (e.doclet.kind === 'function') {

494

e.doclet.isFunction = true;

495

}

496

},

497

498

parseComplete: function(e) {

499

const duration = Date.now() - this.startTime;

500

console.log(`Parse complete in ${duration}ms: ${e.doclets.length} doclets`);

501

}

502

};

503

```

504

505

## Usage Examples

506

507

### Loading Plugins

508

509

```javascript

510

const plugins = require('jsdoc/plugins');

511

const parser = require('jsdoc/src/parser').createParser();

512

513

// Install plugins

514

const pluginPaths = [

515

'plugins/markdown',

516

'plugins/overloadHelper',

517

'./my-custom-plugin'

518

];

519

520

plugins.installPlugins(pluginPaths, parser);

521

```

522

523

### Plugin Testing

524

525

```javascript

526

// test-plugin.js

527

const testPlugin = {

528

handlers: {

529

newDoclet: function(e) {

530

e.doclet.tested = true;

531

}

532

}

533

};

534

535

// Install and test

536

const parser = require('jsdoc/src/parser').createParser();

537

parser.on('newDoclet', testPlugin.handlers.newDoclet);

538

539

const doclets = parser.parse(['test-file.js']);

540

console.log('All doclets tested:', doclets.every(d => d.tested));

541

```

542

543

### Plugin Development Best Practices

544

545

```javascript

546

// good-plugin.js

547

exports.handlers = {

548

newDoclet: function(e) {

549

// Always check for existence

550

if (e.doclet && e.doclet.description) {

551

// Avoid modifying original strings

552

e.doclet.processedDescription = processDescription(e.doclet.description);

553

}

554

}

555

};

556

557

exports.defineTags = function(dictionary) {

558

// Provide complete tag definitions

559

dictionary.defineTag('customTag', {

560

mustHaveValue: true,

561

canHaveType: false,

562

canHaveName: false,

563

onTagged: function(doclet, tag) {

564

// Validate tag value

565

if (typeof tag.value === 'string' && tag.value.length > 0) {

566

doclet.customTag = tag.value;

567

}

568

}

569

});

570

};

571

572

function processDescription(description) {

573

// Safe processing logic

574

try {

575

return description.replace(/\[note\]/g, '<strong>Note:</strong>');

576

} catch (error) {

577

console.warn('Description processing failed:', error.message);

578

return description;

579

}

580

}

581

```