doc: generator code health (3) (#4850)

This commit is contained in:
Pavel Feldman 2020-12-29 12:12:46 -08:00 committed by GitHub
parent 6697dadca2
commit 9817d1095a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 2115 additions and 718 deletions

2730
types/types.d.ts vendored

File diff suppressed because it is too large Load diff

View file

@ -91,9 +91,9 @@ class MDOutline {
clazz.visit(item => patchLinks(item, item.spec, classesMap, membersMap, linkRenderer)); clazz.visit(item => patchLinks(item, item.spec, classesMap, membersMap, linkRenderer));
} }
renderComments() { generateSourceCodeComments() {
for (const clazz of this.classesArray) for (const clazz of this.classesArray)
clazz.visit(item => item.comment = renderLinksForSourceCode(item.spec)); clazz.visit(item => item.comment = generateSourceCodeComment(item.spec));
} }
} }
@ -127,13 +127,13 @@ function extractComments(item) {
/** /**
* @param {MarkdownNode[]} spec * @param {MarkdownNode[]} spec
*/ */
function renderLinksForSourceCode(spec) { function generateSourceCodeComment(spec) {
const comments = (spec || []).filter(n => n.type !== 'gen' && !n.type.startsWith('h') && (n.type !== 'li' || n.liType !== 'default')).map(c => md.clone(c)); const comments = (spec || []).filter(n => n.type !== 'gen' && !n.type.startsWith('h') && (n.type !== 'li' || n.liType !== 'default')).map(c => md.clone(c));
md.visitAll(comments, node => { md.visitAll(comments, node => {
if (node.liType === 'bullet') if (node.liType === 'bullet')
node.liType = 'default'; node.liType = 'default';
}); });
return md.render(comments); return md.render(comments, 120);
} }
/** /**
@ -149,24 +149,24 @@ function patchLinks(item, spec, classesMap, membersMap, linkRenderer) {
md.visitAll(spec, node => { md.visitAll(spec, node => {
if (!node.text) if (!node.text)
return; return;
node.text = node.text.replace(/\[`((?:event|method|property): [^\]]+)`\]/g, (_, p1) => { node.text = node.text.replace(/\[`((?:event|method|property): [^\]]+)`\]/g, (match, p1) => {
const member = membersMap.get(p1); const member = membersMap.get(p1);
return linkRenderer({ member }); return linkRenderer({ member }) || match;
}); });
node.text = node.text.replace(/\[`(param|option): ([^\]]+)`\]/g, (_, p1, p2) => { node.text = node.text.replace(/\[`(param|option): ([^\]]+)`\]/g, (match, p1, p2) => {
const context = { const context = {
clazz: item instanceof Documentation.Class ? item : undefined, clazz: item instanceof Documentation.Class ? item : undefined,
member: item instanceof Documentation.Member ? item : undefined, member: item instanceof Documentation.Member ? item : undefined,
}; };
if (p1 === 'param') if (p1 === 'param')
return linkRenderer({ ...context, param: p2 }); return linkRenderer({ ...context, param: p2 }) || match;
if (p1 === 'option') if (p1 === 'option')
return linkRenderer({ ...context, option: p2 }); return linkRenderer({ ...context, option: p2 }) || match;
}); });
node.text = node.text.replace(/\[([\w]+)\]/, (match, p1) => { node.text = node.text.replace(/\[([\w]+)\]/, (match, p1) => {
const clazz = classesMap.get(p1); const clazz = classesMap.get(p1);
if (clazz) if (clazz)
return linkRenderer({ clazz }); return linkRenderer({ clazz }) || match;
return match; return match;
}); });
}); });

View file

@ -17,15 +17,29 @@
// @ts-check // @ts-check
const path = require('path'); const path = require('path');
const Documentation = require('./Documentation');
const { MDOutline } = require('./MDBuilder'); const { MDOutline } = require('./MDBuilder');
const PROJECT_DIR = path.join(__dirname, '..', '..'); const PROJECT_DIR = path.join(__dirname, '..', '..');
{ {
const { documentation } = new MDOutline(path.join(PROJECT_DIR, 'docs-src', 'api-body.md'), path.join(PROJECT_DIR, 'docs-src', 'api-params.md')); const outline = new MDOutline(path.join(PROJECT_DIR, 'docs-src', 'api-body.md'), path.join(PROJECT_DIR, 'docs-src', 'api-params.md'));
const result = serialize(documentation); outline.renderLinks(item => {
const { clazz, member, param, option } = item;
if (param)
return `\`${param}\``;
if (option)
return `\`${option}\``;
if (clazz)
return `\`${clazz.name}\``;
});
outline.generateSourceCodeComments();
const result = serialize(outline);
console.log(JSON.stringify(result)); console.log(JSON.stringify(result));
} }
/**
* @param {Documentation} documentation
*/
function serialize(documentation) { function serialize(documentation) {
const result = {}; const result = {};
for (const clazz of documentation.classesArray) for (const clazz of documentation.classesArray)
@ -33,6 +47,9 @@ function serialize(documentation) {
return result; return result;
} }
/**
* @param {Documentation.Class} clazz
*/
function serializeClass(clazz) { function serializeClass(clazz) {
const result = { name: clazz.name }; const result = { name: clazz.name };
if (clazz.extends) if (clazz.extends)
@ -58,11 +75,13 @@ function serializeClass(clazz) {
return result; return result;
} }
/**
* @param {Documentation.Member} member
*/
function serializeMember(member) { function serializeMember(member) {
const result = { ...member }; const result = /** @type {any} */ ({ ...member });
sanitize(result); sanitize(result);
result.args = {}; result.args = {};
delete member.clazz;
for (const arg of member.argsArray) for (const arg of member.argsArray)
result.args[arg.name] = serializeProperty(arg); result.args[arg.name] = serializeProperty(arg);
if (member.type) if (member.type)
@ -83,6 +102,7 @@ function sanitize(result) {
delete result.args; delete result.args;
delete result.argsArray; delete result.argsArray;
delete result.templates; delete result.templates;
delete result.clazz;
if (result.properties && !Object.keys(result.properties).length) if (result.properties && !Object.keys(result.properties).length)
delete result.properties; delete result.properties;
if (result.comment === '') if (result.comment === '')

View file

@ -58,7 +58,7 @@ let hadChanges = false;
return createMemberLink(`${member.clazz.varName}.${member.name}`); return createMemberLink(`${member.clazz.varName}.${member.name}`);
throw new Error('Unknown member kind ' + member.kind); throw new Error('Unknown member kind ' + member.kind);
}); });
outline.renderComments(); outline.generateSourceCodeComments();
documentation = outline.documentation; documentation = outline.documentation;
// Root module types are overridden. // Root module types are overridden.

View file

@ -156,12 +156,13 @@ function parse(content) {
/** /**
* @param {MarkdownNode[]} nodes * @param {MarkdownNode[]} nodes
* @param {number=} maxColumns
*/ */
function render(nodes) { function render(nodes, maxColumns) {
const result = []; const result = [];
let lastNode; let lastNode;
for (let node of nodes) { for (let node of nodes) {
innerRenderMdNode(node, lastNode, result); innerRenderMdNode(node, lastNode, result, maxColumns);
lastNode = node; lastNode = node;
} }
return result.join('\n'); return result.join('\n');
@ -170,9 +171,10 @@ function render(nodes) {
/** /**
* @param {MarkdownNode} node * @param {MarkdownNode} node
* @param {MarkdownNode} lastNode * @param {MarkdownNode} lastNode
* @param {number=} maxColumns
* @param {string[]} result * @param {string[]} result
*/ */
function innerRenderMdNode(node, lastNode, result) { function innerRenderMdNode(node, lastNode, result, maxColumns) {
const newLine = () => { const newLine = () => {
if (result[result.length - 1] !== '') if (result[result.length - 1] !== '')
result.push(''); result.push('');
@ -184,7 +186,7 @@ function innerRenderMdNode(node, lastNode, result) {
result.push(`${'#'.repeat(depth)} ${node.text}`); result.push(`${'#'.repeat(depth)} ${node.text}`);
let lastNode = node; let lastNode = node;
for (const child of node.children || []) { for (const child of node.children || []) {
innerRenderMdNode(child, lastNode, result); innerRenderMdNode(child, lastNode, result, maxColumns);
lastNode = child; lastNode = child;
} }
} }
@ -193,7 +195,7 @@ function innerRenderMdNode(node, lastNode, result) {
const bothComments = node.text.startsWith('>') && lastNode && lastNode.type === 'text' && lastNode.text.startsWith('>'); const bothComments = node.text.startsWith('>') && lastNode && lastNode.type === 'text' && lastNode.text.startsWith('>');
if (!bothComments && lastNode && lastNode.text) if (!bothComments && lastNode && lastNode.text)
newLine(); newLine();
result.push(node.text); result.push(wrapText(node.text, maxColumns));
} }
if (node.type === 'code') { if (node.type === 'code') {
@ -220,7 +222,7 @@ function innerRenderMdNode(node, lastNode, result) {
case 'default': char = '-'; break; case 'default': char = '-'; break;
case 'ordinal': char = '1.'; break; case 'ordinal': char = '1.'; break;
} }
result.push(`${indent}${char} ${node.text}`); result.push(`${indent}${char} ${wrapText(node.text, maxColumns, indent + ' '.repeat(char.length + 1))}`);
for (const child of node.children || []) for (const child of node.children || [])
visit(child, indent + ' '); visit(child, indent + ' ');
}; };
@ -228,6 +230,45 @@ function innerRenderMdNode(node, lastNode, result) {
} }
} }
/**
* @param {string} text
*/
function tokenizeText(text) {
const links = [];
// Don't wrap simple links with spaces.
text = text.replace(/\[[^\]]+\]/g, match => {
links.push(match);
return `[${links.length - 1}]`;
});
return text.split(' ').map(c => c.replace(/\[(\d+)\]/g, (_, p1) => links[+p1]));
}
/**
* @param {string} text
* @param {number=} maxColumns
* @param {string=} indent
*/
function wrapText(text, maxColumns = 0, indent = '') {
if (!maxColumns)
return text;
const lines = [];
maxColumns -= indent.length;
const words = tokenizeText(text);
let line = '';
for (const word of words) {
if (line.length && line.length + word.length < maxColumns) {
line += ' ' + word;
} else {
if (line)
lines.push(line);
line = (lines.length ? indent : '') + word;
}
}
if (line)
lines.push(line);
return lines.join('\n');
}
/** /**
* @param {MarkdownNode} node * @param {MarkdownNode} node
*/ */