JSDoc 插件扩展 #

插件系统概述 #

JSDoc 提供了强大的插件系统,允许开发者扩展和定制文档生成功能。

text
┌─────────────────────────────────────────────────────────────┐
│                     JSDoc 插件系统                           │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │ 内置插件     │  │ 第三方插件   │  │ 自定义插件   │         │
│  │             │  │             │  │             │         │
│  │ • markdown  │  │ • docdash   │  │ • 标签插件   │         │
│  │ • summare   │  │ • better-   │  │ • 处理插件   │         │
│  │ • sourcetag │  │   docs      │  │ • 模板插件   │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│                                                              │
└─────────────────────────────────────────────────────────────┘

内置插件 #

plugins/markdown #

支持 Markdown 格式:

javascript
// jsdoc.json
{
  "plugins": ["plugins/markdown"]
}

配置选项:

javascript
{
  "plugins": ["plugins/markdown"],
  "markdown": {
    "hardwrap": true,        // 硬换行
    "idInHeadings": true     // 标题添加 ID
  }
}

使用示例:

javascript
/**
 * 用户服务
 * 
 * 提供用户相关的操作:
 * - 创建用户
 * - 更新用户
 * - 删除用户
 * 
 * @module UserService
 */

/**
 * 创建用户
 * 
 * **注意**:用户名必须唯一
 * 
 * @param {Object} data - 用户数据
 * @returns {Promise<User>}
 * 
 * @example
 * ```javascript
 * const user = await createUser({
 *   name: 'John',
 *   email: 'john@example.com'
 * });
 * ```
 */
async function createUser(data) {}

plugins/summare #

自动生成描述摘要:

javascript
// jsdoc.json
{
  "plugins": ["plugins/summare"]
}

效果:

javascript
/**
 * 计算两个数字的和。该函数接受两个数字参数,返回它们的和。
 * @param {number} a - 第一个数字
 * @param {number} b - 第二个数字
 * @returns {number} 两数之和
 */

// 自动提取第一句作为摘要
// 摘要: 计算两个数字的和。

plugins/sourcetag #

支持 @source 标签:

javascript
// jsdoc.json
{
  "plugins": ["plugins/sourcetag"]
}

使用示例:

javascript
/**
 * @source https://github.com/user/repo/blob/main/src/utils.js#L10-L20
 */
function utility() {}

plugins/testdoc #

支持测试文档:

javascript
// jsdoc.json
{
  "plugins": ["plugins/testdoc"]
}

plugins/overloadHelper #

支持函数重载:

javascript
// jsdoc.json
{
  "plugins": ["plugins/overloadHelper"]
}

使用示例:

javascript
/**
 * @overload
 * @param {string} input
 * @returns {string}
 */

/**
 * @overload
 * @param {number} input
 * @returns {number}
 */

/**
 * 处理输入
 * @param {string|number} input
 * @returns {string|number}
 */
function process(input) {
  return input;
}

第三方插件 #

docdash 模板 #

流行的文档模板:

bash
npm install docdash --save-dev
javascript
// jsdoc.json
{
  "opts": {
    "template": "node_modules/docdash"
  },
  "docdash": {
    "static": true,
    "sort": true,
    "search": true,
    "collapse": true,
    "wrap": true,
    "typedefs": true,
    "navLevel": 2,
    "private": false,
    "removeQuotes": "none",
    "scripts": [],
    "menu": {
      "GitHub": {
        "href": "https://github.com/user/repo",
        "target": "_blank"
      }
    }
  }
}

better-docs 模板 #

功能丰富的文档模板:

bash
npm install better-docs --save-dev
javascript
// jsdoc.json
{
  "opts": {
    "template": "node_modules/better-docs"
  },
  "plugins": [
    "node_modules/better-docs/category"
  ],
  "better-docs": {
    "name": "My Project",
    "logo": "./logo.png",
    "navigation": [
      {
        "label": "GitHub",
        "href": "https://github.com/user/repo"
      }
    ]
  }
}

使用分类标签:

javascript
/**
 * @category User
 */
function getUser() {}

/**
 * @category User
 */
function createUser() {}

/**
 * @category Auth
 */
function login() {}

jsdoc-plugin-typescript #

TypeScript 支持:

bash
npm install jsdoc-plugin-typescript --save-dev
javascript
// jsdoc.json
{
  "plugins": [
    "node_modules/jsdoc-plugin-typescript"
  ],
  "source": {
    "includePattern": ".+\\.ts$"
  }
}

tui-jsdoc-template #

美观的文档模板:

bash
npm install tui-jsdoc-template --save-dev
javascript
// jsdoc.json
{
  "opts": {
    "template": "node_modules/tui-jsdoc-template"
  },
  "templates": {
    "logo": {
      "url": "./logo.png",
      "width": "150px"
    },
    "name": "My API",
    "footerText": "© 2024 My Company"
  }
}

自定义插件开发 #

插件结构 #

javascript
// my-plugin.js

/**
 * JSDoc 插件模块
 * @module my-plugin
 */

// 1. 定义事件处理器
exports.eventHandlers = {
  // 处理解析完成事件
  parseComplete: function(e) {
    console.log('Parsing complete!');
  }
};

// 2. 定义标签
exports.defineTags = function(dictionary) {
  dictionary.defineTag('category', {
    mustHaveValue: true,
    onTagged: function(doclet, tag) {
      doclet.category = tag.value;
    }
  });
};

// 3. 定义节点访问器
exports.astNodeVisitor = {
  visitNode: function(node, e, parser, currentSourceName) {
    // 处理 AST 节点
  }
};

// 4. 定义文档处理器
exports.handlers = {
  newDoclet: function(e) {
    // 处理新的 doclet
  }
};

定义自定义标签 #

javascript
// plugins/customTags.js

exports.defineTags = function(dictionary) {
  // @category 标签
  dictionary.defineTag('category', {
    mustHaveValue: true,
    onTagged: function(doclet, tag) {
      doclet.category = tag.value;
    }
  });

  // @security 标签
  dictionary.defineTag('security', {
    mustHaveValue: true,
    onTagged: function(doclet, tag) {
      if (!doclet.security) {
        doclet.security = [];
      }
      doclet.security.push(tag.value);
    }
  });

  // @author 标签扩展
  dictionary.defineTag('author', {
    mustHaveValue: true,
    onTagged: function(doclet, tag) {
      if (!doclet.authors) {
        doclet.authors = [];
      }
      doclet.authors.push({
        name: tag.value.split('<')[0].trim(),
        email: tag.value.match(/<(.+)>/)?.[1]
      });
    }
  });

  // @version 标签
  dictionary.defineTag('version', {
    mustHaveValue: true,
    onTagged: function(doclet, tag) {
      doclet.version = tag.value;
    }
  });
};

使用自定义标签:

javascript
/**
 * 获取用户信息
 * @category User
 * @security authenticated
 * @author John Doe <john@example.com>
 * @version 1.0.0
 * @param {number} id - 用户 ID
 * @returns {Promise<User>}
 */
async function getUser(id) {}

处理 Doclet #

javascript
// plugins/docletProcessor.js

exports.handlers = {
  // 处理新创建的 doclet
  newDoclet: function(e) {
    const doclet = e.doclet;
    
    // 添加创建时间
    if (!doclet.createdAt) {
      doclet.createdAt = new Date().toISOString();
    }
    
    // 自动添加示例
    if (doclet.kind === 'function' && !doclet.examples) {
      doclet.examples = [`// TODO: Add example for ${doclet.name}`];
    }
    
    // 处理描述
    if (doclet.description) {
      doclet.description = doclet.description.trim();
    }
  },
  
  // 处理解析完成
  parseComplete: function(e) {
    console.log(`Parsed ${e.doclets.length} doclets`);
  },
  
  // 处理文件开始
  fileBegin: function(e) {
    console.log(`Processing: ${e.filename}`);
  },
  
  // 处理文件结束
  fileComplete: function(e) {
    console.log(`Completed: ${e.filename}`);
  }
};

AST 节点访问 #

javascript
// plugins/astVisitor.js

exports.astNodeVisitor = {
  visitNode: function(node, e, parser, currentSourceName) {
    // 处理函数声明
    if (node.type === 'FunctionDeclaration') {
      // 自动推断参数类型
      node.params.forEach(param => {
        if (param.type === 'Identifier') {
          console.log(`Found parameter: ${param.name}`);
        }
      });
    }
    
    // 处理类声明
    if (node.type === 'ClassDeclaration') {
      console.log(`Found class: ${node.id?.name}`);
    }
    
    // 处理变量声明
    if (node.type === 'VariableDeclaration') {
      node.declarations.forEach(decl => {
        if (decl.id.type === 'Identifier') {
          console.log(`Found variable: ${decl.id.name}`);
        }
      });
    }
  }
};

完整插件示例 #

javascript
// plugins/apiDocs.js

/**
 * API 文档增强插件
 * @module apiDocs
 */

const path = require('path');

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

  dictionary.defineTag('method', {
    mustHaveValue: true,
    onTagged: function(doclet, tag) {
      doclet.httpMethod = tag.value.toUpperCase();
    }
  });

  dictionary.defineTag('middleware', {
    mustHaveValue: false,
    onTagged: function(doclet, tag) {
      doclet.middleware = tag.value || true;
    }
  });

  dictionary.defineTag('auth', {
    mustHaveValue: true,
    onTagged: function(doclet, tag) {
      doclet.auth = tag.value;
    }
  });
};

exports.handlers = {
  newDoclet: function(e) {
    const doclet = e.doclet;
    
    // 为 API 端点添加默认值
    if (doclet.endpoint) {
      if (!doclet.httpMethod) {
        doclet.httpMethod = 'GET';
      }
      
      // 生成完整 URL
      if (doclet.meta?.path) {
        const basePath = process.env.API_BASE_PATH || '/api';
        doclet.fullPath = path.join(basePath, doclet.endpoint);
      }
    }
    
    // 添加安全标记
    if (doclet.auth) {
      doclet.security = {
        type: doclet.auth,
        required: true
      };
    }
  },
  
  parseComplete: function(e) {
    // 按 endpoint 分组
    const endpoints = {};
    e.doclets.forEach(doclet => {
      if (doclet.endpoint) {
        if (!endpoints[doclet.endpoint]) {
          endpoints[doclet.endpoint] = [];
        }
        endpoints[doclet.endpoint].push(doclet);
      }
    });
    
    console.log(`Generated docs for ${Object.keys(endpoints).length} endpoints`);
  }
};

使用 API 插件:

javascript
/**
 * 获取用户列表
 * @endpoint /users
 * @method GET
 * @auth jwt
 * @param {number} [page=1] - 页码
 * @param {number} [limit=10] - 每页数量
 * @returns {Promise<{ list: User[], total: number }>}
 */
router.get('/users', authMiddleware, getUsers);

/**
 * 创建用户
 * @endpoint /users
 * @method POST
 * @auth jwt
 * @middleware validateUser
 * @param {Object} user - 用户数据
 * @returns {Promise<User>}
 */
router.post('/users', authMiddleware, validateUser, createUser);

插件配置 #

注册插件 #

javascript
// jsdoc.json
{
  "plugins": [
    "./plugins/customTags.js",
    "./plugins/apiDocs.js"
  ]
}

插件选项 #

javascript
// jsdoc.json
{
  "plugins": [
    "./plugins/customTags.js"
  ],
  "customTags": {
    "categories": ["User", "Auth", "API"],
    "securityLevels": ["public", "authenticated", "admin"]
  }
}

在插件中读取配置:

javascript
// plugins/customTags.js
exports.handlers = {
  parseBegin: function(e) {
    const options = e.options.customTags || {};
    console.log('Categories:', options.categories);
  }
};

插件发布 #

包结构 #

text
jsdoc-plugin-myplugin/
├── package.json
├── index.js
├── README.md
└── LICENSE

package.json #

javascript
{
  "name": "jsdoc-plugin-myplugin",
  "version": "1.0.0",
  "description": "A JSDoc plugin for ...",
  "main": "index.js",
  "keywords": [
    "jsdoc",
    "plugin"
  ],
  "peerDependencies": {
    "jsdoc": ">=3.6.0"
  },
  "license": "MIT"
}

index.js #

javascript
/**
 * @module jsdoc-plugin-myplugin
 */

exports.defineTags = function(dictionary) {
  // ...
};

exports.handlers = {
  // ...
};

插件调试 #

启用调试模式 #

javascript
// jsdoc.json
{
  "opts": {
    "debug": true,
    "verbose": true
  }
}

日志输出 #

javascript
// plugins/debug.js
const util = require('util');

exports.handlers = {
  newDoclet: function(e) {
    console.log('Doclet:', util.inspect(e.doclet, { depth: 2 }));
  }
};

错误处理 #

javascript
exports.handlers = {
  newDoclet: function(e) {
    try {
      // 处理逻辑
    } catch (error) {
      console.error(`Error processing ${e.doclet.name}:`, error.message);
    }
  }
};

常用插件列表 #

插件 用途 安装
docdash 文档模板 npm i docdash
better-docs 增强模板 npm i better-docs
tui-jsdoc-template 美观模板 npm i tui-jsdoc-template
jsdoc-plugin-typescript TypeScript 支持 npm i jsdoc-plugin-typescript
jsdoc-vuejs Vue.js 支持 npm i jsdoc-vuejs
jsdoc-react React 支持 npm i jsdoc-react
jsdoc-to-markdown Markdown 输出 npm i jsdoc-to-markdown

下一步 #

现在你已经掌握了 JSDoc 插件扩展,接下来学习 最佳实践 了解如何编写高质量的文档!

最后更新:2026-03-29