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

plugins.mddocs/

Plugin System

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.

Capabilities

Plugin Installation

Functions for loading and installing plugins into the JSDoc parser.

/**
 * Install an array of plugins into a parser instance
 * @param plugins - Array of plugin module paths
 * @param parser - Parser instance to install plugins into
 */
function installPlugins(plugins: string[], parser: Parser): void;

Plugin Interface

Standard interface that all JSDoc plugins must implement.

interface Plugin {
  /**
   * Event handlers for parser events
   * Maps event names to handler functions
   */
  handlers?: {
    [eventName: string]: (e: any) => void;
  };

  /**
   * Define custom JSDoc tags
   * @param dictionary - Tag dictionary to add definitions to
   */
  defineTags?(dictionary: TagDictionary): void;

  /**
   * AST node visitor function
   * Called for each AST node during parsing
   */
  astNodeVisitor?: (
    node: ASTNode,
    e: any,
    parser: Parser,
    currentSourceName: string
  ) => void;
}

Built-in Plugins

Markdown Plugin

Processes Markdown syntax in JSDoc comments and converts to HTML.

// Plugin: plugins/markdown.js
const plugin = {
  handlers: {
    newDoclet: function(e) {
      // Process Markdown in description
      if (e.doclet.description) {
        e.doclet.description = markdown.parse(e.doclet.description);
      }
    }
  }
};

Usage in configuration:

{
  "plugins": ["plugins/markdown"]
}

Comment Convert Plugin

Converts certain comment types to JSDoc comments.

// Plugin: plugins/commentConvert.js
const plugin = {
  handlers: {
    beforeParse: function(e) {
      // Convert // comments to /** */ format in certain cases
      e.source = convertComments(e.source);
    }
  }
};

Escape HTML Plugin

Escapes HTML content in JSDoc comments to prevent XSS.

// Plugin: plugins/escapeHtml.js
const plugin = {
  handlers: {
    newDoclet: function(e) {
      if (e.doclet.description) {
        e.doclet.description = escapeHtml(e.doclet.description);
      }
    }
  }
};

Overload Helper Plugin

Helps document function overloads by grouping related doclets.

// Plugin: plugins/overloadHelper.js
const plugin = {
  handlers: {
    parseComplete: function(e) {
      // Group overloaded functions
      groupOverloads(e.doclets);
    }
  }
};

Source Tag Plugin

Adds source code information to doclets.

// Plugin: plugins/sourcetag.js
const plugin = {
  defineTags: function(dictionary) {
    dictionary.defineTag('source', {
      onTagged: function(doclet, tag) {
        doclet.source = tag.value;
      }
    });
  }
};

Summarize Plugin

Creates summary descriptions from the first sentence of descriptions.

// Plugin: plugins/summarize.js
const plugin = {
  handlers: {
    newDoclet: function(e) {
      if (e.doclet.description && !e.doclet.summary) {
        e.doclet.summary = extractFirstSentence(e.doclet.description);
      }
    }
  }
};

Event Dumper Plugin

Debug plugin that logs all parser events to console.

// Plugin: plugins/eventDumper.js
const plugin = {
  handlers: {
    parseBegin: (e) => console.log('parseBegin:', e),
    fileBegin: (e) => console.log('fileBegin:', e),
    jsdocCommentFound: (e) => console.log('jsdocCommentFound:', e),
    newDoclet: (e) => console.log('newDoclet:', e),
    parseComplete: (e) => console.log('parseComplete:', e)
  }
};

Escape HTML Plugin

Escapes HTML content in JSDoc comments to prevent XSS attacks.

// Plugin: plugins/escapeHtml.js
const plugin = {
  handlers: {
    newDoclet: function(e) {
      if (e.doclet.description) {
        e.doclet.description = escapeHtml(e.doclet.description);
      }
      if (e.doclet.summary) {
        e.doclet.summary = escapeHtml(e.doclet.summary);
      }
    }
  }
};

Comments Only Plugin

Processes only comment content, filtering out code elements.

// Plugin: plugins/commentsOnly.js
const plugin = {
  handlers: {
    beforeParse: function(e) {
      // Filter source to include only comments
      e.source = extractCommentsOnly(e.source);
    }
  }
};

Template Helper Plugins

Plugins that enhance template functionality:

Rails Template Plugin

Rails-style template syntax support for JSDoc templates.

// Plugin: plugins/railsTemplate.js
const plugin = {
  handlers: {
    parseBegin: function(e) {
      // Configure Rails-style template syntax
      configureRailsTemplates();
    }
  }
};

Underscore Template Plugin

Underscore.js template integration for JSDoc.

// Plugin: plugins/underscore.js
const plugin = {
  handlers: {
    parseBegin: function(e) {
      // Set up Underscore.js template engine
      configureUnderscoreTemplates();
    }
  }
};

Partial Template Plugin

Partial template support for modular template organization.

// Plugin: plugins/partial.js
const plugin = {
  handlers: {
    processingComplete: function(e) {
      // Process partial templates
      processPartialTemplates(e.doclets);
    }
  }
};

Text Processing Plugins

Plugins for text manipulation and formatting:

Shout Plugin

Converts specific text elements to uppercase for emphasis.

// Plugin: plugins/shout.js
const plugin = {
  handlers: {
    newDoclet: function(e) {
      if (e.doclet.description) {
        e.doclet.description = processShoutText(e.doclet.description);
      }
    }
  }
};

Custom Plugin Development

Basic Plugin Structure

// my-custom-plugin.js
exports.handlers = {
  newDoclet: function(e) {
    if (e.doclet.kind === 'function') {
      // Custom processing for functions
      e.doclet.customProperty = 'processed';
    }
  }
};

exports.defineTags = function(dictionary) {
  dictionary.defineTag('custom', {
    mustHaveValue: true,
    onTagged: function(doclet, tag) {
      doclet.customTag = tag.value;
    }
  });
};

Event Handler Plugin

// event-logger-plugin.js
const fs = require('fs');

exports.handlers = {
  parseBegin: function(e) {
    console.log('Starting parse of:', e.sourcefiles.length, 'files');
  },
  
  newDoclet: function(e) {
    if (e.doclet.kind === 'function' && !e.doclet.description) {
      console.warn(`Undocumented function: ${e.doclet.longname}`);
    }
  },
  
  parseComplete: function(e) {
    const stats = {
      total: e.doclets.length,
      documented: e.doclets.filter(d => !d.undocumented).length,
      functions: e.doclets.filter(d => d.kind === 'function').length,
      classes: e.doclets.filter(d => d.kind === 'class').length
    };
    
    fs.writeFileSync('doc-stats.json', JSON.stringify(stats, null, 2));
  }
};

Custom Tag Plugin

// version-tag-plugin.js
exports.defineTags = function(dictionary) {
  dictionary.defineTag('version', {
    mustHaveValue: true,
    canHaveType: false,
    canHaveName: false,
    onTagged: function(doclet, tag) {
      doclet.version = tag.value;
    }
  });
  
  dictionary.defineTag('since', {
    mustHaveValue: true,
    onTagged: function(doclet, tag) {
      doclet.since = tag.value;
    }
  });
};

AST Node Visitor Plugin

// ast-visitor-plugin.js
exports.astNodeVisitor = function(node, e, parser, currentSourceName) {
  // Process specific node types
  if (node.type === 'CallExpression') {
    // Track function calls
    if (node.callee && node.callee.name === 'deprecated') {
      // Mark enclosing function as deprecated
      const doclet = parser._getDocletById(node.parent.nodeId);
      if (doclet) {
        doclet.deprecated = true;
      }
    }
  }
  
  if (node.type === 'ClassDeclaration') {
    // Process class declarations
    console.log(`Found class: ${node.id ? node.id.name : 'anonymous'}`);
  }
};

Advanced Plugin with Configuration

// configurable-plugin.js
let pluginConfig = {
  logLevel: 'info',
  outputFile: 'plugin-output.json',
  processPrivate: false
};

exports.handlers = {
  parseBegin: function(e) {
    // Load configuration from JSDoc config
    const jsdocConfig = require('jsdoc/env').conf;
    if (jsdocConfig.plugins && jsdocConfig.plugins.configurablePlugin) {
      pluginConfig = { ...pluginConfig, ...jsdocConfig.plugins.configurablePlugin };
    }
  },
  
  newDoclet: function(e) {
    if (!pluginConfig.processPrivate && e.doclet.access === 'private') {
      return;
    }
    
    if (pluginConfig.logLevel === 'debug') {
      console.log('Processing doclet:', e.doclet.longname);
    }
  }
};

Plugin Configuration

In JSDoc Configuration File

{
  "plugins": [
    "plugins/markdown",
    "plugins/overloadHelper",
    "./custom-plugins/my-plugin"
  ],
  "custom-plugin-config": {
    "logLevel": "debug",
    "outputFile": "custom-output.json"
  }
}

Plugin Loading Order

Plugins are loaded in the order specified in the configuration:

  1. Plugin handlers are registered with the parser
  2. Custom tags are defined in the tag dictionary
  3. AST node visitors are added to the parser
  4. Parser events fire handlers in registration order

Plugin Event Reference

Available Events

// Parser lifecycle events
interface ParserEvents {
  parseBegin: { sourcefiles: string[] };
  fileBegin: { filename: string };
  beforeParse: { filename: string; source: string };
  jsdocCommentFound: { 
    comment: string; 
    lineno: number; 
    filename: string; 
  };
  symbolFound: any;
  newDoclet: { doclet: Doclet };
  fileComplete: { filename: string };
  parseComplete: { 
    sourcefiles: string[]; 
    doclets: Doclet[]; 
  };
  processingComplete: { doclets: Doclet[] };
}

Event Handler Examples

// Comprehensive event handling
exports.handlers = {
  parseBegin: function(e) {
    this.startTime = Date.now();
    console.log(`Starting parse of ${e.sourcefiles.length} files`);
  },
  
  fileBegin: function(e) {
    console.log(`Processing file: ${e.filename}`);
  },
  
  jsdocCommentFound: function(e) {
    // Validate comment format
    if (!e.comment.includes('@')) {
      console.warn(`Minimal JSDoc at ${e.filename}:${e.lineno}`);
    }
  },
  
  newDoclet: function(e) {
    // Enhance doclets
    if (e.doclet.kind === 'function') {
      e.doclet.isFunction = true;
    }
  },
  
  parseComplete: function(e) {
    const duration = Date.now() - this.startTime;
    console.log(`Parse complete in ${duration}ms: ${e.doclets.length} doclets`);
  }
};

Usage Examples

Loading Plugins

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

// Install plugins
const pluginPaths = [
  'plugins/markdown',
  'plugins/overloadHelper',
  './my-custom-plugin'
];

plugins.installPlugins(pluginPaths, parser);

Plugin Testing

// test-plugin.js
const testPlugin = {
  handlers: {
    newDoclet: function(e) {
      e.doclet.tested = true;
    }
  }
};

// Install and test
const parser = require('jsdoc/src/parser').createParser();
parser.on('newDoclet', testPlugin.handlers.newDoclet);

const doclets = parser.parse(['test-file.js']);
console.log('All doclets tested:', doclets.every(d => d.tested));

Plugin Development Best Practices

// good-plugin.js
exports.handlers = {
  newDoclet: function(e) {
    // Always check for existence
    if (e.doclet && e.doclet.description) {
      // Avoid modifying original strings
      e.doclet.processedDescription = processDescription(e.doclet.description);
    }
  }
};

exports.defineTags = function(dictionary) {
  // Provide complete tag definitions
  dictionary.defineTag('customTag', {
    mustHaveValue: true,
    canHaveType: false,
    canHaveName: false,
    onTagged: function(doclet, tag) {
      // Validate tag value
      if (typeof tag.value === 'string' && tag.value.length > 0) {
        doclet.customTag = tag.value;
      }
    }
  });
};

function processDescription(description) {
  // Safe processing logic
  try {
    return description.replace(/\[note\]/g, '<strong>Note:</strong>');
  } catch (error) {
    console.warn('Description processing failed:', error.message);
    return description;
  }
}

docs

cli.md

configuration.md

environment.md

index.md

parser.md

plugins.md

templates.md

utilities.md

tile.json