CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-jsdoc

API documentation generator for JavaScript that parses source code and JSDoc comments to produce HTML documentation

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

templates.mddocs/

Template System

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.

Capabilities

Template Class

Core template class for loading and rendering templates with data.

class Template {
  /**
   * Create a new Template instance
   * @param filepath - Path to templates directory
   */
  constructor(filepath: string);

  /**
   * Load template from file and compile
   * @param file - Template filename
   * @returns Compiled template function
   */
  load(file: string): Function;

  /**
   * Render partial template with data
   * @param file - Template filename  
   * @param data - Template data object
   * @returns Rendered HTML string
   */
  partial(file: string, data: object): string;

  /**
   * Render complete template with layout
   * @param file - Template filename
   * @param data - Template data object  
   * @returns Rendered HTML string with layout applied
   */
  render(file: string, data: object): string;

  /**
   * Template settings for custom tag syntax
   */
  settings: {
    evaluate: RegExp;    // Code execution tags: <?js ... ?>
    interpolate: RegExp; // Variable interpolation: <?js= ... ?>
    escape: RegExp;      // HTML escaping: <?js~ ... ?>
  };

  /**
   * Layout template path (optional)
   */
  layout: string | null;

  /**
   * Template cache for performance
   */
  cache: { [file: string]: Function };
}

Template Publish Function

Standard interface for template entry points.

/**
 * Main template publish function interface
 * @param data - TaffyDB collection of all doclets
 * @param opts - JSDoc options and configuration
 * @param tutorials - Tutorial root object (optional)
 * @returns Promise or undefined
 */
function publish(
  data: TaffyDB,
  opts: JSDocOptions,
  tutorials?: TutorialRoot
): Promise<void> | void;

interface JSDocOptions {
  destination: string;        // Output directory
  encoding: string;          // File encoding
  template: string;          // Template path
  readme?: string;           // README content
  package?: object;          // Package information
  access?: string[];         // Access levels to include
  private?: boolean;         // Include private members
  query?: object;            // Custom query parameters
}

interface TutorialRoot {
  title: string;
  children: Tutorial[];
}

Built-in Templates

Default Template

The standard HTML template included with JSDoc.

Location: templates/default/

Key Files:

  • publish.js - Main template entry point
  • tmpl/ - Template fragments
  • static/ - CSS, JavaScript, and other assets

Features:

  • Responsive HTML layout
  • Search functionality
  • Syntax highlighting
  • Cross-reference navigation
  • Tutorial integration

Haruki Template

Minimalist template with clean design.

Location: templates/haruki/

Features:

  • Simple, clean interface
  • Minimal styling
  • Focus on content readability

Silent Template

Template that generates no output (for testing/validation).

Location: templates/silent/

Usage: Useful for syntax checking without generating files.

Template Development

Basic Template Structure

my-template/
├── publish.js          # Main entry point
├── tmpl/              # Template fragments
│   ├── layout.tmpl    # Main layout
│   ├── container.tmpl # Content container
│   └── ...
├── static/            # Static assets
│   ├── styles/        # CSS files
│   ├── scripts/       # JavaScript files
│   └── fonts/         # Font files
└── README.md          # Template documentation

Basic Publish Function

// publish.js
const doop = require('jsdoc/util/doop');
const env = require('jsdoc/env');
const fs = require('jsdoc/fs');
const helper = require('jsdoc/util/templateHelper');
const path = require('jsdoc/path');
const { taffy } = require('@jsdoc/salty');
const template = require('jsdoc/template');

let data;
let view;
let outdir = path.normalize(env.opts.destination);

function find(spec) {
  return helper.find(data, spec);
}

exports.publish = function(taffyData, opts, tutorials) {
  data = taffyData;
  
  const conf = env.conf.templates || {};
  conf.default = conf.default || {};

  const templatePath = path.normalize(env.opts.template);
  view = new template.Template(path.join(templatePath, 'tmpl'));

  // Generate output files
  generateFiles();
};

function generateFiles() {
  const classes = find({ kind: 'class' });
  const namespaces = find({ kind: 'namespace' });
  
  classes.forEach(function(c) {
    generateClassDoc(c);
  });
  
  namespaces.forEach(function(n) {
    generateNamespaceDoc(n);
  });
}

Advanced Template with Helpers

// publish.js with helpers
const helper = require('jsdoc/util/templateHelper');

// Template helper functions
const htmlsafe = helper.htmlsafe;
const linkto = helper.linkto;
const resolveAuthorLinks = helper.resolveAuthorLinks;

function buildNav(members) {
  let nav = '<ul>';
  
  if (members.classes.length) {
    nav += '<li>Classes<ul>';
    members.classes.forEach(function(c) {
      nav += `<li>${linkto(c.longname, c.name)}</li>`;
    });
    nav += '</ul></li>';
  }
  
  if (members.namespaces.length) {
    nav += '<li>Namespaces<ul>';
    members.namespaces.forEach(function(n) {
      nav += `<li>${linkto(n.longname, n.name)}</li>`;
    });
    nav += '</ul></li>';
  }
  
  nav += '</ul>';
  return nav;
}

function generateSourceFiles() {
  const sourceFiles = {};
  
  data().each(function(doclet) {
    if (doclet.meta) {
      const sourcePath = getPathFromDoclet(doclet);
      sourceFiles[sourcePath] = {
        shortened: sourcePath,
        resolved: sourcePath
      };
    }
  });
  
  return sourceFiles;
}

exports.publish = function(taffyData, opts, tutorials) {
  data = taffyData;
  
  const members = helper.getMembers(data);
  const sourceFiles = generateSourceFiles();
  const nav = buildNav(members);
  
  // Generate index page
  const indexHtml = view.render('index.tmpl', {
    title: opts.package?.name || 'API Documentation',
    nav: nav,
    members: members
  });
  
  fs.writeFileSync(path.join(outdir, 'index.html'), indexHtml, 'utf8');
};

Template Fragment Examples

Layout Template (tmpl/layout.tmpl)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title><?js= title ?></title>
    <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
    <div id="main">
        <h1 class="page-title"><?js= title ?></h1>
        <?js= content ?>
    </div>
    
    <nav>
        <?js= this.nav ?>
    </nav>
    
    <script src="scripts/prettify/prettify.js"></script>
    <script>prettyPrint();</script>
</body>
</html>

Class Template (tmpl/class.tmpl)

<?js
var self = this;
var isGlobalPage;

docs.forEach(function(doc, i) {
?>

<section>
    <header>
        <h2><?js if (doc.ancestors && doc.ancestors.length) { ?>
            <span class="ancestors"><?js= doc.ancestors.join('') ?></span>
        <?js } ?>
        <?js= doc.name ?>
        <?js if (doc.variation) { ?>
            <sup class="variation"><?js= doc.variation ?></sup>
        <?js } ?></h2>
        <?js if (doc.classdesc) { ?>
            <div class="class-description"><?js= doc.classdesc ?></div>
        <?js } ?>
    </header>

    <article>
        <div class="container-overview">
        <?js if (doc.kind === 'class' && doc.examples && doc.examples.length) { ?>
            <h3>Example<?js= doc.examples.length > 1? 's':'' ?></h3>
            <?js= this.partial('examples.tmpl', doc.examples) ?>
        <?js } ?>
        </div>

        <?js if (doc.augments && doc.augments.length) { ?>
            <h3 class="subsection-title">Extends</h3>
            <?js= this.partial('augments.tmpl', doc.augments) ?>
        <?js } ?>

        <?js if (doc.requires && doc.requires.length) { ?>
            <h3 class="subsection-title">Requires</h3>
            <ul><?js doc.requires.forEach(function(r) { ?>
                <li><?js= self.linkto(r, r) ?></li>
            <?js }); ?></ul>
        <?js } ?>
    </article>
</section>

<?js }); ?>

Template Helper Functions

Core Helpers

JSDoc provides built-in helper functions for common template tasks.

const helper = require('jsdoc/util/templateHelper');

/**
 * Create HTML-safe string
 * @param str - String to make HTML-safe
 * @returns HTML-escaped string
 */
function htmlsafe(str: string): string;

/**
 * Create link to symbol
 * @param longname - Symbol longname
 * @param linkText - Link text (optional)
 * @returns HTML link element
 */
function linkto(longname: string, linkText?: string): string;

/**
 * Find doclets matching specification
 * @param data - TaffyDB data collection
 * @param spec - Search specification
 * @returns Array of matching doclets
 */
function find(data: TaffyDB, spec: object): Doclet[];

/**
 * Get organized members from data
 * @param data - TaffyDB data collection
 * @returns Organized member object
 */
function getMembers(data: TaffyDB): {
  classes: Doclet[];
  externals: Doclet[];
  events: Doclet[];
  globals: Doclet[];
  mixins: Doclet[];
  modules: Doclet[];
  namespaces: Doclet[];
  tutorials: Doclet[];
  interfaces: Doclet[];
};

/**
 * Resolve author links in text
 * @param str - String with author information
 * @returns String with resolved links
 */
function resolveAuthorLinks(str: string): string;

/**
 * Get ancestor links for symbol
 * @param data - TaffyDB data collection
 * @param doclet - Current doclet
 * @returns Array of ancestor link strings
 */
function getAncestorLinks(data: TaffyDB, doclet: Doclet): string[];

Custom Helper Development

// Custom template helpers
function formatDate(date) {
  return new Date(date).toLocaleDateString();
}

function getTypeNames(type) {
  if (!type || !type.names) return 'unknown';
  return type.names.join(' | ');
}

function buildParameterTable(params) {
  if (!params || !params.length) return '';
  
  let table = '<table class="params"><thead><tr><th>Name</th><th>Type</th><th>Description</th></tr></thead><tbody>';
  
  params.forEach(function(param) {
    table += '<tr>';
    table += `<td>${param.name}</td>`;
    table += `<td>${getTypeNames(param.type)}</td>`;
    table += `<td>${param.description || ''}</td>`;
    table += '</tr>';
  });
  
  table += '</tbody></table>';
  return table;
}

// Use in templates
exports.publish = function(taffyData, opts, tutorials) {
  // Make helpers available to templates
  view.formatDate = formatDate;
  view.getTypeNames = getTypeNames;
  view.buildParameterTable = buildParameterTable;
};

Template Configuration

Template Selection

# Use specific template
jsdoc -t templates/custom src/

# Use template from node_modules
jsdoc -t node_modules/my-jsdoc-template src/

Template Configuration in JSDoc Config

{
  "opts": {
    "template": "./templates/custom"
  },
  "templates": {
    "cleverLinks": false,
    "monospaceLinks": false,
    "dateFormat": "MMMM Do YYYY, h:mm:ss a",
    "outputSourceFiles": true,
    "outputSourcePath": true,
    "systemName": "My API",
    "footer": "Copyright 2023",
    "copyright": "Copyright 2023 My Company",
    "includeDate": true,
    "navType": "vertical",
    "theme": "cerulean",
    "linenums": true,
    "collapseSymbols": false,
    "inverseNav": true,
    "protocol": "html://",
    "methodHeadingReturns": false
  }
}

Usage Examples

Simple Custom Template

// simple-template/publish.js
const fs = require('jsdoc/fs');
const path = require('jsdoc/path');
const { taffy } = require('@jsdoc/salty');

exports.publish = function(data, opts) {
  const outdir = path.normalize(opts.destination);
  
  // Generate simple HTML
  let html = `
    <!DOCTYPE html>
    <html>
    <head><title>API Documentation</title></head>
    <body>
      <h1>API Documentation</h1>
      <ul>
  `;
  
  data({ kind: ['class', 'function', 'namespace'] }).each(function(doclet) {
    html += `<li><strong>${doclet.name}</strong> - ${doclet.description || 'No description'}</li>`;
  });
  
  html += `
      </ul>
    </body>
    </html>
  `;
  
  fs.writeFileSync(path.join(outdir, 'index.html'), html, 'utf8');
};

JSON Output Template

// json-template/publish.js
const fs = require('jsdoc/fs');
const path = require('jsdoc/path');

exports.publish = function(data, opts) {
  const outdir = path.normalize(opts.destination);
  
  const docs = data().get().map(function(doclet) {
    return {
      name: doclet.name,
      longname: doclet.longname,
      kind: doclet.kind,
      description: doclet.description,
      params: doclet.params,
      returns: doclet.returns,
      examples: doclet.examples
    };
  });
  
  const output = {
    generated: new Date().toISOString(),
    package: opts.package,
    docs: docs
  };
  
  fs.writeFileSync(
    path.join(outdir, 'api.json'),
    JSON.stringify(output, null, 2),
    'utf8'
  );
};

Markdown Template

// markdown-template/publish.js
const fs = require('jsdoc/fs');
const path = require('jsdoc/path');

exports.publish = function(data, opts) {
  const outdir = path.normalize(opts.destination);
  
  let markdown = `# ${opts.package?.name || 'API'} Documentation\n\n`;
  
  const classes = data({ kind: 'class' }).get();
  const functions = data({ kind: 'function' }).get();
  
  if (classes.length) {
    markdown += '## Classes\n\n';
    classes.forEach(function(cls) {
      markdown += `### ${cls.name}\n\n`;
      if (cls.description) {
        markdown += `${cls.description}\n\n`;
      }
    });
  }
  
  if (functions.length) {
    markdown += '## Functions\n\n';
    functions.forEach(function(fn) {
      markdown += `### ${fn.name}\n\n`;
      if (fn.description) {
        markdown += `${fn.description}\n\n`;
      }
      if (fn.params && fn.params.length) {
        markdown += '**Parameters:**\n\n';
        fn.params.forEach(function(param) {
          markdown += `- \`${param.name}\` - ${param.description || ''}\n`;
        });
        markdown += '\n';
      }
    });
  }
  
  fs.writeFileSync(path.join(outdir, 'README.md'), markdown, 'utf8');
};

docs

cli.md

configuration.md

environment.md

index.md

parser.md

plugins.md

templates.md

utilities.md

tile.json