dev: cleanup docgen and add method logging

This commit is contained in:
KernelDeimos 2024-12-04 12:53:25 -05:00
parent 0b36e72c80
commit 5fa0a0263d
3 changed files with 162 additions and 61 deletions

View File

@ -102,6 +102,7 @@ class ModuleDoc extends Doc {
class ServiceDoc extends Doc { class ServiceDoc extends Doc {
_construct () { _construct () {
this.listeners = []; this.listeners = [];
this.methods = [];
} }
provide_comment (comment) { provide_comment (comment) {
@ -127,6 +128,24 @@ class ServiceDoc extends Doc {
}); });
} }
provide_method (method) {
const parsed_comment = doctrine.parse(method.comment, { unwrap: true });
const params = [];
for ( const tag of parsed_comment.tags ) {
if ( tag.title !== 'param' ) continue;
const name = tag.name;
const desc = tag.description;
params.push({ name, desc })
}
this.methods.push({
...method,
comment: parsed_comment.description,
params,
});
}
toMarkdown ({ hl, out } = { hl: 1 }) { toMarkdown ({ hl, out } = { hl: 1 }) {
out = out ?? new Out(); out = out ?? new Out();
@ -151,6 +170,23 @@ class ServiceDoc extends Doc {
} }
} }
if ( this.methods.length > 0 ) {
out.h(hl + 1, 'Methods');
for ( const method of this.methods ) {
out.h(hl + 2, '`' + method.key + '`');
out (method.comment + '\n\n');
if ( method.params.length > 0 ) {
out.h(hl + 3, 'Parameters');
for ( const param of method.params ) {
out(`- **${param.name}:** ${param.desc}\n`);
}
out.lf();
}
}
}
return out.text(); return out.text();
} }
} }

View File

@ -6,6 +6,7 @@ const rootdir = path_.resolve(process.argv[2] ?? '.');
const parser = require('@babel/parser'); const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default; const traverse = require('@babel/traverse').default;
const { ModuleDoc } = require("./defs"); const { ModuleDoc } = require("./defs");
const processors = require("./processors");
const doc_module = new ModuleDoc(); const doc_module = new ModuleDoc();
@ -27,71 +28,41 @@ for ( const file of files ) {
console.log('file', file); console.log('file', file);
const code = fs.readFileSync(path_.join(rootdir, file), 'utf8'); const code = fs.readFileSync(path_.join(rootdir, file), 'utf8');
const firstLine = code.slice(0, code.indexOf('\n'));
let metadata = {};
const METADATA_PREFIX = '// METADATA // ';
if ( firstLine.startsWith(METADATA_PREFIX) ) {
metadata = JSON.parse(firstLine.slice(METADATA_PREFIX.length));
}
const ast = parser.parse(code); const ast = parser.parse(code);
traverse(ast, {
CallExpression (path) {
const callee = path.get('callee');
if ( ! callee.isIdentifier() ) return;
if ( callee.node.name === 'require' ) { const traverse_callbacks = {};
doc_module.requires.push(path.node.arguments[0].value); const context = {
} type,
}, doc_module,
ClassDeclaration (path) { filename: file,
const node = path.node; };
const name = node.id.name; for ( const processor of processors ) {
if ( processor.match(context) ) {
// Skip utility classes (for now) for ( const key in processor.traverse ) {
if ( name !== file.slice(0, -3) ) { if ( ! traverse_callbacks[key] ) {
return; traverse_callbacks[key] = [];
}
const comment = (node.leadingComments && (
node.leadingComments.length < 1 ? '' :
node.leadingComments[node.leadingComments.length - 1]
)) ?? '';
let doc_item = doc_module;
if ( type !== 'module' ) {
doc_item = doc_module.add_service();
}
doc_item.name = name;
if ( comment !== '' ) {
doc_item.provide_comment(comment);
}
if ( type === 'module' ) {
return;
}
if ( comment !== '' ) {
doc_item.provide_comment(comment);
// to_service_add_comment(def_service, comment);
}
console.log('class', name);
path.node.body.body.forEach(member => {
const key = member.key.name ?? member.key.value;
const comment = member.leadingComments?.[0]?.value ?? '';
if ( key.startsWith('__on_') ) {
// 2nd argument is always an object destructuring;
// we want the list of keys in the object:
const params = member.params?.[1]?.properties ?? [];
doc_item.provide_listener({
key: key.slice(5),
comment,
params,
});
} }
console.log(member.type, key, member.leadingComments); traverse_callbacks[key].push(processor.traverse[key]);
}); }
} }
}) }
for ( const key in traverse_callbacks ) {
traverse(ast, {
[key] (path) {
for ( const callback of traverse_callbacks[key] ) {
callback(path, context);
}
}
});
}
} }
const outfile = path_.join(rootdir, 'README.md'); const outfile = path_.join(rootdir, 'README.md');

View File

@ -0,0 +1,94 @@
const processors = [];
processors.push({
title: 'track all require calls',
match () { return true; },
traverse: {
CallExpression (path, context) {
const callee = path.get('callee');
if ( ! callee.isIdentifier() ) return;
if ( callee.node.name === 'require' ) {
context.doc_module.requires.push(path.node.arguments[0].value);
}
}
}
});
processors.push({
title: 'get leading comment',
match () { return true; },
traverse: {
ClassDeclaration (path, context) {
const node = path.node;
const comment = (node.leadingComments && (
node.leadingComments.length < 1 ? '' :
node.leadingComments[node.leadingComments.length - 1]
)) ?? '';
context.comment = comment;
}
}
});
processors.push({
title: 'provide name and comment for modules and services',
match (context) {
return context.type === 'module' || context.type === 'service';
},
traverse: {
ClassDeclaration (path, context) {
context.doc_item = context.doc_module;
if ( context.type === 'service' ) {
context.doc_item = context.doc_module.add_service();
}
context.doc_item.name = path.node.id.name;
context.doc_item.provide_comment(context.comment);
}
}
});
processors.push({
title: 'provide methods and listeners for services',
match (context) {
return context.type === 'service';
},
traverse: {
ClassDeclaration (path, context) {
path.node.body.body.forEach(member => {
if ( member.type !== 'ClassMethod' ) return;
const key = member.key.name ?? member.key.value;
const comment = member.leadingComments?.[0]?.value ?? '';
if ( key.startsWith('__on_') ) {
// 2nd argument is always an object destructuring;
// we want the list of keys in the object:
const params = member.params?.[1]?.properties ?? [];
context.doc_item.provide_listener({
key: key.slice(5),
comment,
params,
});
} else {
// Method overrides
if ( key.startsWith('_') ) return;
// Private methods
if ( key.endsWith('_') ) return;
const params = member.params ?? [];
context.doc_item.provide_method({
key,
comment,
params,
});
}
});
}
}
});
module.exports = processors;