0
# Template System
1
2
JSDoc's template system generates HTML documentation from parsed doclets using a flexible template engine. It supports custom templates, layouts, helper functions, and complete control over output formatting.
3
4
## Capabilities
5
6
### Template Class
7
8
Core template class for loading and rendering templates with data.
9
10
```javascript { .api }
11
class Template {
12
/**
13
* Create a new Template instance
14
* @param filepath - Path to templates directory
15
*/
16
constructor(filepath: string);
17
18
/**
19
* Load template from file and compile
20
* @param file - Template filename
21
* @returns Compiled template function
22
*/
23
load(file: string): Function;
24
25
/**
26
* Render partial template with data
27
* @param file - Template filename
28
* @param data - Template data object
29
* @returns Rendered HTML string
30
*/
31
partial(file: string, data: object): string;
32
33
/**
34
* Render complete template with layout
35
* @param file - Template filename
36
* @param data - Template data object
37
* @returns Rendered HTML string with layout applied
38
*/
39
render(file: string, data: object): string;
40
41
/**
42
* Template settings for custom tag syntax
43
*/
44
settings: {
45
evaluate: RegExp; // Code execution tags: <?js ... ?>
46
interpolate: RegExp; // Variable interpolation: <?js= ... ?>
47
escape: RegExp; // HTML escaping: <?js~ ... ?>
48
};
49
50
/**
51
* Layout template path (optional)
52
*/
53
layout: string | null;
54
55
/**
56
* Template cache for performance
57
*/
58
cache: { [file: string]: Function };
59
}
60
```
61
62
### Template Publish Function
63
64
Standard interface for template entry points.
65
66
```javascript { .api }
67
/**
68
* Main template publish function interface
69
* @param data - TaffyDB collection of all doclets
70
* @param opts - JSDoc options and configuration
71
* @param tutorials - Tutorial root object (optional)
72
* @returns Promise or undefined
73
*/
74
function publish(
75
data: TaffyDB,
76
opts: JSDocOptions,
77
tutorials?: TutorialRoot
78
): Promise<void> | void;
79
80
interface JSDocOptions {
81
destination: string; // Output directory
82
encoding: string; // File encoding
83
template: string; // Template path
84
readme?: string; // README content
85
package?: object; // Package information
86
access?: string[]; // Access levels to include
87
private?: boolean; // Include private members
88
query?: object; // Custom query parameters
89
}
90
91
interface TutorialRoot {
92
title: string;
93
children: Tutorial[];
94
}
95
```
96
97
## Built-in Templates
98
99
### Default Template
100
101
The standard HTML template included with JSDoc.
102
103
**Location**: `templates/default/`
104
105
**Key Files**:
106
- `publish.js` - Main template entry point
107
- `tmpl/` - Template fragments
108
- `static/` - CSS, JavaScript, and other assets
109
110
**Features**:
111
- Responsive HTML layout
112
- Search functionality
113
- Syntax highlighting
114
- Cross-reference navigation
115
- Tutorial integration
116
117
### Haruki Template
118
119
Minimalist template with clean design.
120
121
**Location**: `templates/haruki/`
122
123
**Features**:
124
- Simple, clean interface
125
- Minimal styling
126
- Focus on content readability
127
128
### Silent Template
129
130
Template that generates no output (for testing/validation).
131
132
**Location**: `templates/silent/`
133
134
**Usage**: Useful for syntax checking without generating files.
135
136
## Template Development
137
138
### Basic Template Structure
139
140
```
141
my-template/
142
├── publish.js # Main entry point
143
├── tmpl/ # Template fragments
144
│ ├── layout.tmpl # Main layout
145
│ ├── container.tmpl # Content container
146
│ └── ...
147
├── static/ # Static assets
148
│ ├── styles/ # CSS files
149
│ ├── scripts/ # JavaScript files
150
│ └── fonts/ # Font files
151
└── README.md # Template documentation
152
```
153
154
### Basic Publish Function
155
156
```javascript
157
// publish.js
158
const doop = require('jsdoc/util/doop');
159
const env = require('jsdoc/env');
160
const fs = require('jsdoc/fs');
161
const helper = require('jsdoc/util/templateHelper');
162
const path = require('jsdoc/path');
163
const { taffy } = require('@jsdoc/salty');
164
const template = require('jsdoc/template');
165
166
let data;
167
let view;
168
let outdir = path.normalize(env.opts.destination);
169
170
function find(spec) {
171
return helper.find(data, spec);
172
}
173
174
exports.publish = function(taffyData, opts, tutorials) {
175
data = taffyData;
176
177
const conf = env.conf.templates || {};
178
conf.default = conf.default || {};
179
180
const templatePath = path.normalize(env.opts.template);
181
view = new template.Template(path.join(templatePath, 'tmpl'));
182
183
// Generate output files
184
generateFiles();
185
};
186
187
function generateFiles() {
188
const classes = find({ kind: 'class' });
189
const namespaces = find({ kind: 'namespace' });
190
191
classes.forEach(function(c) {
192
generateClassDoc(c);
193
});
194
195
namespaces.forEach(function(n) {
196
generateNamespaceDoc(n);
197
});
198
}
199
```
200
201
### Advanced Template with Helpers
202
203
```javascript
204
// publish.js with helpers
205
const helper = require('jsdoc/util/templateHelper');
206
207
// Template helper functions
208
const htmlsafe = helper.htmlsafe;
209
const linkto = helper.linkto;
210
const resolveAuthorLinks = helper.resolveAuthorLinks;
211
212
function buildNav(members) {
213
let nav = '<ul>';
214
215
if (members.classes.length) {
216
nav += '<li>Classes<ul>';
217
members.classes.forEach(function(c) {
218
nav += `<li>${linkto(c.longname, c.name)}</li>`;
219
});
220
nav += '</ul></li>';
221
}
222
223
if (members.namespaces.length) {
224
nav += '<li>Namespaces<ul>';
225
members.namespaces.forEach(function(n) {
226
nav += `<li>${linkto(n.longname, n.name)}</li>`;
227
});
228
nav += '</ul></li>';
229
}
230
231
nav += '</ul>';
232
return nav;
233
}
234
235
function generateSourceFiles() {
236
const sourceFiles = {};
237
238
data().each(function(doclet) {
239
if (doclet.meta) {
240
const sourcePath = getPathFromDoclet(doclet);
241
sourceFiles[sourcePath] = {
242
shortened: sourcePath,
243
resolved: sourcePath
244
};
245
}
246
});
247
248
return sourceFiles;
249
}
250
251
exports.publish = function(taffyData, opts, tutorials) {
252
data = taffyData;
253
254
const members = helper.getMembers(data);
255
const sourceFiles = generateSourceFiles();
256
const nav = buildNav(members);
257
258
// Generate index page
259
const indexHtml = view.render('index.tmpl', {
260
title: opts.package?.name || 'API Documentation',
261
nav: nav,
262
members: members
263
});
264
265
fs.writeFileSync(path.join(outdir, 'index.html'), indexHtml, 'utf8');
266
};
267
```
268
269
### Template Fragment Examples
270
271
#### Layout Template (`tmpl/layout.tmpl`)
272
273
```html
274
<!DOCTYPE html>
275
<html lang="en">
276
<head>
277
<meta charset="utf-8">
278
<title><?js= title ?></title>
279
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
280
</head>
281
<body>
282
<div id="main">
283
<h1 class="page-title"><?js= title ?></h1>
284
<?js= content ?>
285
</div>
286
287
<nav>
288
<?js= this.nav ?>
289
</nav>
290
291
<script src="scripts/prettify/prettify.js"></script>
292
<script>prettyPrint();</script>
293
</body>
294
</html>
295
```
296
297
#### Class Template (`tmpl/class.tmpl`)
298
299
```html
300
<?js
301
var self = this;
302
var isGlobalPage;
303
304
docs.forEach(function(doc, i) {
305
?>
306
307
<section>
308
<header>
309
<h2><?js if (doc.ancestors && doc.ancestors.length) { ?>
310
<span class="ancestors"><?js= doc.ancestors.join('') ?></span>
311
<?js } ?>
312
<?js= doc.name ?>
313
<?js if (doc.variation) { ?>
314
<sup class="variation"><?js= doc.variation ?></sup>
315
<?js } ?></h2>
316
<?js if (doc.classdesc) { ?>
317
<div class="class-description"><?js= doc.classdesc ?></div>
318
<?js } ?>
319
</header>
320
321
<article>
322
<div class="container-overview">
323
<?js if (doc.kind === 'class' && doc.examples && doc.examples.length) { ?>
324
<h3>Example<?js= doc.examples.length > 1? 's':'' ?></h3>
325
<?js= this.partial('examples.tmpl', doc.examples) ?>
326
<?js } ?>
327
</div>
328
329
<?js if (doc.augments && doc.augments.length) { ?>
330
<h3 class="subsection-title">Extends</h3>
331
<?js= this.partial('augments.tmpl', doc.augments) ?>
332
<?js } ?>
333
334
<?js if (doc.requires && doc.requires.length) { ?>
335
<h3 class="subsection-title">Requires</h3>
336
<ul><?js doc.requires.forEach(function(r) { ?>
337
<li><?js= self.linkto(r, r) ?></li>
338
<?js }); ?></ul>
339
<?js } ?>
340
</article>
341
</section>
342
343
<?js }); ?>
344
```
345
346
## Template Helper Functions
347
348
### Core Helpers
349
350
JSDoc provides built-in helper functions for common template tasks.
351
352
```javascript { .api }
353
const helper = require('jsdoc/util/templateHelper');
354
355
/**
356
* Create HTML-safe string
357
* @param str - String to make HTML-safe
358
* @returns HTML-escaped string
359
*/
360
function htmlsafe(str: string): string;
361
362
/**
363
* Create link to symbol
364
* @param longname - Symbol longname
365
* @param linkText - Link text (optional)
366
* @returns HTML link element
367
*/
368
function linkto(longname: string, linkText?: string): string;
369
370
/**
371
* Find doclets matching specification
372
* @param data - TaffyDB data collection
373
* @param spec - Search specification
374
* @returns Array of matching doclets
375
*/
376
function find(data: TaffyDB, spec: object): Doclet[];
377
378
/**
379
* Get organized members from data
380
* @param data - TaffyDB data collection
381
* @returns Organized member object
382
*/
383
function getMembers(data: TaffyDB): {
384
classes: Doclet[];
385
externals: Doclet[];
386
events: Doclet[];
387
globals: Doclet[];
388
mixins: Doclet[];
389
modules: Doclet[];
390
namespaces: Doclet[];
391
tutorials: Doclet[];
392
interfaces: Doclet[];
393
};
394
395
/**
396
* Resolve author links in text
397
* @param str - String with author information
398
* @returns String with resolved links
399
*/
400
function resolveAuthorLinks(str: string): string;
401
402
/**
403
* Get ancestor links for symbol
404
* @param data - TaffyDB data collection
405
* @param doclet - Current doclet
406
* @returns Array of ancestor link strings
407
*/
408
function getAncestorLinks(data: TaffyDB, doclet: Doclet): string[];
409
```
410
411
### Custom Helper Development
412
413
```javascript
414
// Custom template helpers
415
function formatDate(date) {
416
return new Date(date).toLocaleDateString();
417
}
418
419
function getTypeNames(type) {
420
if (!type || !type.names) return 'unknown';
421
return type.names.join(' | ');
422
}
423
424
function buildParameterTable(params) {
425
if (!params || !params.length) return '';
426
427
let table = '<table class="params"><thead><tr><th>Name</th><th>Type</th><th>Description</th></tr></thead><tbody>';
428
429
params.forEach(function(param) {
430
table += '<tr>';
431
table += `<td>${param.name}</td>`;
432
table += `<td>${getTypeNames(param.type)}</td>`;
433
table += `<td>${param.description || ''}</td>`;
434
table += '</tr>';
435
});
436
437
table += '</tbody></table>';
438
return table;
439
}
440
441
// Use in templates
442
exports.publish = function(taffyData, opts, tutorials) {
443
// Make helpers available to templates
444
view.formatDate = formatDate;
445
view.getTypeNames = getTypeNames;
446
view.buildParameterTable = buildParameterTable;
447
};
448
```
449
450
## Template Configuration
451
452
### Template Selection
453
454
```bash
455
# Use specific template
456
jsdoc -t templates/custom src/
457
458
# Use template from node_modules
459
jsdoc -t node_modules/my-jsdoc-template src/
460
```
461
462
### Template Configuration in JSDoc Config
463
464
```json
465
{
466
"opts": {
467
"template": "./templates/custom"
468
},
469
"templates": {
470
"cleverLinks": false,
471
"monospaceLinks": false,
472
"dateFormat": "MMMM Do YYYY, h:mm:ss a",
473
"outputSourceFiles": true,
474
"outputSourcePath": true,
475
"systemName": "My API",
476
"footer": "Copyright 2023",
477
"copyright": "Copyright 2023 My Company",
478
"includeDate": true,
479
"navType": "vertical",
480
"theme": "cerulean",
481
"linenums": true,
482
"collapseSymbols": false,
483
"inverseNav": true,
484
"protocol": "html://",
485
"methodHeadingReturns": false
486
}
487
}
488
```
489
490
## Usage Examples
491
492
### Simple Custom Template
493
494
```javascript
495
// simple-template/publish.js
496
const fs = require('jsdoc/fs');
497
const path = require('jsdoc/path');
498
const { taffy } = require('@jsdoc/salty');
499
500
exports.publish = function(data, opts) {
501
const outdir = path.normalize(opts.destination);
502
503
// Generate simple HTML
504
let html = `
505
<!DOCTYPE html>
506
<html>
507
<head><title>API Documentation</title></head>
508
<body>
509
<h1>API Documentation</h1>
510
<ul>
511
`;
512
513
data({ kind: ['class', 'function', 'namespace'] }).each(function(doclet) {
514
html += `<li><strong>${doclet.name}</strong> - ${doclet.description || 'No description'}</li>`;
515
});
516
517
html += `
518
</ul>
519
</body>
520
</html>
521
`;
522
523
fs.writeFileSync(path.join(outdir, 'index.html'), html, 'utf8');
524
};
525
```
526
527
### JSON Output Template
528
529
```javascript
530
// json-template/publish.js
531
const fs = require('jsdoc/fs');
532
const path = require('jsdoc/path');
533
534
exports.publish = function(data, opts) {
535
const outdir = path.normalize(opts.destination);
536
537
const docs = data().get().map(function(doclet) {
538
return {
539
name: doclet.name,
540
longname: doclet.longname,
541
kind: doclet.kind,
542
description: doclet.description,
543
params: doclet.params,
544
returns: doclet.returns,
545
examples: doclet.examples
546
};
547
});
548
549
const output = {
550
generated: new Date().toISOString(),
551
package: opts.package,
552
docs: docs
553
};
554
555
fs.writeFileSync(
556
path.join(outdir, 'api.json'),
557
JSON.stringify(output, null, 2),
558
'utf8'
559
);
560
};
561
```
562
563
### Markdown Template
564
565
```javascript
566
// markdown-template/publish.js
567
const fs = require('jsdoc/fs');
568
const path = require('jsdoc/path');
569
570
exports.publish = function(data, opts) {
571
const outdir = path.normalize(opts.destination);
572
573
let markdown = `# ${opts.package?.name || 'API'} Documentation\n\n`;
574
575
const classes = data({ kind: 'class' }).get();
576
const functions = data({ kind: 'function' }).get();
577
578
if (classes.length) {
579
markdown += '## Classes\n\n';
580
classes.forEach(function(cls) {
581
markdown += `### ${cls.name}\n\n`;
582
if (cls.description) {
583
markdown += `${cls.description}\n\n`;
584
}
585
});
586
}
587
588
if (functions.length) {
589
markdown += '## Functions\n\n';
590
functions.forEach(function(fn) {
591
markdown += `### ${fn.name}\n\n`;
592
if (fn.description) {
593
markdown += `${fn.description}\n\n`;
594
}
595
if (fn.params && fn.params.length) {
596
markdown += '**Parameters:**\n\n';
597
fn.params.forEach(function(param) {
598
markdown += `- \`${param.name}\` - ${param.description || ''}\n`;
599
});
600
markdown += '\n';
601
}
602
});
603
}
604
605
fs.writeFileSync(path.join(outdir, 'README.md'), markdown, 'utf8');
606
};
607
```