UNPKG

@geekberry/jsdoc-to-md

Version:

# Install `npm install @geekberry/jsdoc-to-md`

137 lines (113 loc) 3.63 kB
const fs = require('fs'); const pathLib = require('path'); const lodash = require('lodash'); const jsdocApi = require('jsdoc-api'); const jsdocParse = require('jsdoc-parse'); /** * @param path {string} - Filename or directory path * @param [stack=[]] {array} - Relative path to root dir * @return {object[]} */ function listPath(path, stack = []) { const list = []; if (fs.statSync(path).isDirectory()) { for (const name of fs.readdirSync(path)) { list.push(...listPath(pathLib.resolve(path, name), [...stack, name])); } } else { list.push({ filename: path, stack }); } return list; } function translateInfo(info) { switch (info.kind) { case 'class': if (info.scope !== 'global') { // sub class as global scope info.scope = 'global'; info.name = `${info.memberof}.${info.name}`; info.memberof = undefined; } break; case 'constructor': info.kind = 'function'; info.name = '**constructor**'; // add '**' to avoid lodash.set not accept 'constructor' over v4.17.20 info.scope = 'instance'; break; case 'member': const paramsLength = lodash.get(info, ['params', 'length']); const returnsLength = lodash.get(info, ['returns', 'length']); if (paramsLength && returnsLength) { info.kind = 'function'; } else if (paramsLength) { info.kind = 'function'; info.role = 'setter'; } else if (returnsLength) { info.kind = 'function'; info.role = 'getter'; } break; case 'constant': info.kind = 'member'; break; case 'function': default: break; } switch (info.scope) { case 'static': info.longname = `${info.memberof}.${info.name}`; break; case 'instance': info.longname = `${info.memberof}.prototype.${info.name}`; break; case 'global': default: info.longname = info.name; break; } delete info.meta; delete info.order; delete info.thisvalue; return info; } /** * Parse '.js' file jsdoc * * @param path {string} - Filename or directory path * @param filter {function} - Filename filter * @return {object} */ function parseJsDoc(path, filter = () => true) { const idToInfo = {}; lodash.forEach(listPath(path), ({ filename, stack }) => { if (!filename.endsWith('.js') || !filter(filename)) { return undefined; } const array = jsdocParse(jsdocApi.explainSync({ files: filename })); array .filter(info => !['protected', 'private'].includes(info.access)) .map(translateInfo) .forEach(info => { const staticLabel = info.scope === 'static' ? '(static)' : ''; const roleLabel = info.role ? `(${info.role})` : ''; const name = `${staticLabel}${info.name}${roleLabel}`; info.stack = [...stack, info.memberof, name].filter(Boolean); info.id = info.stack.join('/'); const parentId = [...stack, info.memberof].filter(Boolean).join('/'); switch (info.scope) { case 'static': lodash.set(idToInfo, [parentId, 'static', name], info); break; case 'instance': lodash.set(idToInfo, [parentId, 'instance', name], info); break; case 'global': default: idToInfo[info.id] = { ...info, ...(idToInfo[info.id] || {}) }; break; } }); }); return idToInfo; } module.exports = parseJsDoc;