mirror of
https://github.com/HeyPuter/puter.git
synced 2025-02-02 23:28:39 +08:00
dev: include library functions in module docgen
This commit is contained in:
parent
5fa0a0263d
commit
5c3a65060d
@ -36,6 +36,7 @@ class ModuleDoc extends Doc {
|
|||||||
_construct () {
|
_construct () {
|
||||||
this.services = [];
|
this.services = [];
|
||||||
this.requires = [];
|
this.requires = [];
|
||||||
|
this.libs = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
add_service () {
|
add_service () {
|
||||||
@ -44,6 +45,12 @@ class ModuleDoc extends Doc {
|
|||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add_lib () {
|
||||||
|
const lib = new LibDoc();
|
||||||
|
this.libs.push(lib);
|
||||||
|
return lib;
|
||||||
|
}
|
||||||
|
|
||||||
ready () {
|
ready () {
|
||||||
this.notes = [];
|
this.notes = [];
|
||||||
const rel_requires = this.requires.filter(r => r.startsWith('../'));
|
const rel_requires = this.requires.filter(r => r.startsWith('../'));
|
||||||
@ -85,6 +92,14 @@ class ModuleDoc extends Doc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( this.libs.length > 0 ) {
|
||||||
|
out.h(hl + 1, 'Libraries');
|
||||||
|
|
||||||
|
for ( const lib of this.libs ) {
|
||||||
|
lib.toMarkdown({ out, hl: hl + 2 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( this.notes.length > 0 ) {
|
if ( this.notes.length > 0 ) {
|
||||||
out.h(hl + 1, 'Notes');
|
out.h(hl + 1, 'Notes');
|
||||||
for ( const note of this.notes ) {
|
for ( const note of this.notes ) {
|
||||||
@ -191,6 +206,57 @@ class ServiceDoc extends Doc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LibDoc extends Doc {
|
||||||
|
_construct () {
|
||||||
|
this.functions = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
provide_function ({ key, comment, params }) {
|
||||||
|
const parsed_comment = doctrine.parse(comment, { unwrap: true });
|
||||||
|
|
||||||
|
const parsed_params = [];
|
||||||
|
for ( const tag of parsed_comment.tags ) {
|
||||||
|
if ( tag.title !== 'param' ) continue;
|
||||||
|
const name = tag.name;
|
||||||
|
const desc = tag.description;
|
||||||
|
parsed_params.push({ name, desc });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.functions.push({
|
||||||
|
key,
|
||||||
|
comment: parsed_comment.description,
|
||||||
|
params: parsed_params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toMarkdown ({ hl, out } = { hl: 1 }) {
|
||||||
|
out = out ?? new Out();
|
||||||
|
|
||||||
|
out.h(hl, this.name);
|
||||||
|
|
||||||
|
console.log('functions?', this.functions);
|
||||||
|
|
||||||
|
if ( this.functions.length > 0 ) {
|
||||||
|
out.h(hl + 1, 'Functions');
|
||||||
|
|
||||||
|
for ( const func of this.functions ) {
|
||||||
|
out.h(hl + 2, '`' + func.key + '`');
|
||||||
|
out(func.comment + '\n\n');
|
||||||
|
|
||||||
|
if ( func.params.length > 0 ) {
|
||||||
|
out.h(hl + 3, 'Parameters');
|
||||||
|
for ( const param of func.params ) {
|
||||||
|
out(`- **${param.name}:** ${param.desc}\n`);
|
||||||
|
}
|
||||||
|
out.lf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
ModuleDoc,
|
ModuleDoc,
|
||||||
ServiceDoc,
|
ServiceDoc,
|
||||||
|
@ -10,9 +10,37 @@ const processors = require("./processors");
|
|||||||
|
|
||||||
const doc_module = new ModuleDoc();
|
const doc_module = new ModuleDoc();
|
||||||
|
|
||||||
// List files in this directory
|
const handle_file = (code, context) => {
|
||||||
const files = fs.readdirSync(rootdir);
|
const ast = parser.parse(code);
|
||||||
for ( const file of files ) {
|
|
||||||
|
const traverse_callbacks = {};
|
||||||
|
for ( const processor of processors ) {
|
||||||
|
if ( processor.match(context) ) {
|
||||||
|
for ( const key in processor.traverse ) {
|
||||||
|
if ( ! traverse_callbacks[key] ) {
|
||||||
|
traverse_callbacks[key] = [];
|
||||||
|
}
|
||||||
|
traverse_callbacks[key].push(processor.traverse[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ( const key in traverse_callbacks ) {
|
||||||
|
traverse(ast, {
|
||||||
|
[key] (path) {
|
||||||
|
context.skip = false;
|
||||||
|
for ( const callback of traverse_callbacks[key] ) {
|
||||||
|
callback(path, context);
|
||||||
|
if ( context.skip ) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Module and class files
|
||||||
|
{
|
||||||
|
const files = fs.readdirSync(rootdir);
|
||||||
|
for ( const file of files ) {
|
||||||
const stat = fs.statSync(path_.join(rootdir, file));
|
const stat = fs.statSync(path_.join(rootdir, file));
|
||||||
if ( stat.isDirectory() ) {
|
if ( stat.isDirectory() ) {
|
||||||
continue;
|
continue;
|
||||||
@ -36,32 +64,44 @@ for ( const file of files ) {
|
|||||||
metadata = JSON.parse(firstLine.slice(METADATA_PREFIX.length));
|
metadata = JSON.parse(firstLine.slice(METADATA_PREFIX.length));
|
||||||
}
|
}
|
||||||
|
|
||||||
const ast = parser.parse(code);
|
|
||||||
|
|
||||||
const traverse_callbacks = {};
|
|
||||||
const context = {
|
const context = {
|
||||||
|
metadata,
|
||||||
type,
|
type,
|
||||||
doc_module,
|
doc_module,
|
||||||
filename: file,
|
filename: file,
|
||||||
};
|
};
|
||||||
for ( const processor of processors ) {
|
|
||||||
if ( processor.match(context) ) {
|
handle_file(code, context);
|
||||||
for ( const key in processor.traverse ) {
|
|
||||||
if ( ! traverse_callbacks[key] ) {
|
|
||||||
traverse_callbacks[key] = [];
|
|
||||||
}
|
}
|
||||||
traverse_callbacks[key].push(processor.traverse[key]);
|
}
|
||||||
|
|
||||||
|
// Library files
|
||||||
|
{
|
||||||
|
const files = fs.readdirSync(path_.join(rootdir, 'lib'));
|
||||||
|
for ( const file of files ) {
|
||||||
|
if ( file.startsWith('_') ) continue;
|
||||||
|
|
||||||
|
const code = fs.readFileSync(path_.join(rootdir, 'lib', 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 doc_item = doc_module.add_lib();
|
||||||
for ( const key in traverse_callbacks ) {
|
doc_item.name = metadata.def ?? file.slice(0, -3);
|
||||||
traverse(ast, {
|
|
||||||
[key] (path) {
|
const context = {
|
||||||
for ( const callback of traverse_callbacks[key] ) {
|
metadata,
|
||||||
callback(path, context);
|
type: 'lib',
|
||||||
}
|
doc_module,
|
||||||
}
|
doc_item,
|
||||||
});
|
filename: file,
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_file(code, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,9 +39,15 @@ processors.push({
|
|||||||
ClassDeclaration (path, context) {
|
ClassDeclaration (path, context) {
|
||||||
context.doc_item = context.doc_module;
|
context.doc_item = context.doc_module;
|
||||||
if ( context.type === 'service' ) {
|
if ( context.type === 'service' ) {
|
||||||
|
// Skip if class name doesn't end with 'Service'
|
||||||
|
if ( ! path.node.id.name.endsWith('Service') ) {
|
||||||
|
context.skip = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
context.doc_item = context.doc_module.add_service();
|
context.doc_item = context.doc_module.add_service();
|
||||||
}
|
}
|
||||||
context.doc_item.name = path.node.id.name;
|
context.doc_item.name = path.node.id.name;
|
||||||
|
if ( context.comment === '' ) return;
|
||||||
context.doc_item.provide_comment(context.comment);
|
context.doc_item.provide_comment(context.comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,4 +97,48 @@ processors.push({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
processors.push({
|
||||||
|
title: 'provide library function documentation',
|
||||||
|
match (context) {
|
||||||
|
return context.type === 'lib';
|
||||||
|
},
|
||||||
|
traverse: {
|
||||||
|
VariableDeclaration (path, context) {
|
||||||
|
// skip non-const declarations
|
||||||
|
if ( path.node.kind !== 'const' ) return;
|
||||||
|
|
||||||
|
// skip declarations with multiple declarators
|
||||||
|
if ( path.node.declarations.length !== 1 ) return;
|
||||||
|
|
||||||
|
// skip declarations without an initializer
|
||||||
|
if ( ! path.node.declarations[0].init ) return;
|
||||||
|
|
||||||
|
// skip declarations that aren't in the root scope
|
||||||
|
if ( path.scope.parent ) return;
|
||||||
|
|
||||||
|
console.log('path.node', path.node.declarations);
|
||||||
|
|
||||||
|
// is it a function?
|
||||||
|
if ( ! ['FunctionExpression', 'ArrowFunctionExpression'].includes(
|
||||||
|
path.node.declarations[0].init.type
|
||||||
|
) ) return;
|
||||||
|
|
||||||
|
// get the name of the function
|
||||||
|
const name = path.node.declarations[0].id.name;
|
||||||
|
|
||||||
|
// get the comment
|
||||||
|
const comment = path.node.leadingComments?.[0]?.value ?? '';
|
||||||
|
|
||||||
|
// get the parameters
|
||||||
|
const params = path.node.declarations[0].init.params ?? [];
|
||||||
|
|
||||||
|
context.doc_item.provide_function({
|
||||||
|
key: name,
|
||||||
|
comment,
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = processors;
|
module.exports = processors;
|
||||||
|
Loading…
Reference in New Issue
Block a user