从代码生成 PlantUML:5 种常见自动化方式
与其一张一张手画类图,不如让代码自己生成。这篇整理 5 种常见方案:TypeDoc、JSDoc、Doxygen、SchemaCrawler、Python 自定义 walker。
方案 1:TypeDoc(TypeScript) 1 npm install --save-dev typedoc
1 2 3 4 5 { "entryPoints" : [ "src/index.ts" ] , "json" : "docs/api/typedoc.json" }
1 npx typedoc --options typedoc.json
输出 typedoc.json —— 包含所有类型信息。
Typedo 默认不带 PlantUML 但有插件:
1 npm install --save-dev typedoc-plantuml
1 2 3 4 { "plugin" : [ "typedoc-plantuml" ] }
自动生成 class 图(手动) 要更直接的方案,写一个 TypeDoc post-render 脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import * as fs from 'fs' ;const data = JSON .parse (fs.readFileSync ('docs/api/typedoc.json' , 'utf-8' ));const classes = data.children .filter ((c : any ) => c.kind === 'class' );let plantuml = '@startuml\n' ;for (const c of classes) { const name = c.name .replace (/([<>])/g , '\\$1' ); plantuml += `class ${name} {\n` ; for (const m of c.children ) { if (m.kind === 'method' ) { plantuml += ` +${m.name} ()\n` ; } else if (m.kind === 'property' ) { plantuml += ` +${m.name} : ${m.type || 'unknown' } \n` ; } } plantuml += '}\n' ; } for (const c of classes) { for (const m of c.children ) { if (m.kind === 'constructor' ) { } } } plantuml += '@enduml\n' ; fs.writeFileSync ('docs/class/auto.puml' , plantuml);
1 2 npx typedoc --options typedoc.json node scripts/gen-plantuml.ts
方案 2:JSDoc + 类图 JSDoc 通过 @inheritDoc 和 tags 表达类型,但不直接生成 PlantUML 。
1 2 3 4 5 6 7 8 9 10 class MyClass { constructor (name ) {} getName ( ) { return this .name } }
生成的 jsdoc HTML 里没有 PlantUML,需要额外插件:
1 npm install --save-dev jsdoc-to-plantuml
1 2 npx jsdoc src -d=docs npx jsdoc-to-plantuml docs/jsdoc.json >docs/class/auto.puml
方案 3:Doxygen(C++ / Java / 注释语言) 1 sudo apt-get install doxygen graphviz
1 2 3 4 5 6 7 # Doxyfile GENERATE_LATEX = NO HAVE_DOT = YES GENERATE_GRAPH = YES CALL_GRAPH = YES PLANTUML_PATH = plantuml.jar JAVADOC_AUTOBRIEF = YES
1 2 3 doxygen -g Doxyfile sed -i 's/#PLANTUML_PATH.*/PLANTUML_PATH = plantuml.jar/' Doxyfile doxygen Doxyfile
Doxygen 直接调用 PlantUML 渲染 @dot / @startuml 块,附 HTML。
C++ 注释里嵌入 @startuml
Doxygen 渲染输出 SVG 自动嵌入。
方案 4:SchemaCrawler(数据库 → PlantUML) 数据库 schema 是天然的关系图。SchemaCrawler 可以生成 SQL DDL、PlantUML、Mermaid:
1 2 3 4 5 6 7 8 9 10 java -jar schemacrawler.jar \ --server=postgresql \ --host=db.internal \ --database=appdb \ --user=app \ --password=*** \ --info-level=standard \ --command =schema \ --output-format=puml \ --output-file=docs/er.puml
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @startuml hide circle entity "users" { *id : BIGINT <<PK>> *email : VARCHAR(255) ... } entity "orders" { *id : BIGINT <<PK>> *user_id : BIGINT <<FK>> } users - orders : user_id @enduml
集成到 CI 1 2 3 4 5 6 7 8 9 10 - name: Generate DB schema run: | java -jar schemacrawler.jar \ --server=postgresql \ --host=localhost \ --database=appdb \ --user=app \ --password=$DB_PWD \ --output-format=puml \ --output-file=docs/db/er.puml
每次数据库 schema 变更,自动生成新的 ER 图。
方案 5:Python 自定义 walker Python 写一个 AST walker:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import astdef walk_class (node ): """Generate PlantUML for a Python class.""" out = f'class {node.name} {{\n' for item in node.body: if isinstance (item, ast.FunctionDef): args = ', ' .join(a.arg for a in item.args.args) visibility = '+' if not item.name.startswith('_' ) else '-' out += f' {visibility} {item.name} ({args} )\n' elif isinstance (item, ast.AnnAssign) and isinstance (item.target, ast.Name): out += f' +{item.target.id } : {ast.unparse(item.annotation)} \n' out += '}\n' return out def walk_module (filepath ): """Walk a Python file and produce PlantUML.""" tree = ast.parse(open (filepath).read()) output = '@startuml\n' for node in ast.walk(tree): if isinstance (node, ast.ClassDef): output += walk_class(node) output += '@enduml\n' return output if __name__ == '__main__' : import sys print (walk_module(sys.argv[1 ]))
1 2 3 4 5 6 7 8 9 $ python walk.py models/user.py @startuml class User { +name: str +email: str +login() +logout () } @enduml
用 inspect 简化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import inspectimport typesdef generate (cls ): out = f'class {cls.__name__} {{\n' for name, value in inspect.getmembers(cls): if name.startswith('_' ): continue if isinstance (value, types.FunctionType): sig = inspect.signature(value) out += f' +{name} {sig} \n' elif isinstance (value, type ): out += f' +{name} : {value.__name__} \n' out += '}\n' return out if __name__ == '__main__' : from user import User print (generate(User))
运行后实时生成。
实战:CI 集成 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 name: Generate docs on: push: paths: ['src/**' , 'app/**' , 'lib/**' ] schedule: - cron: '0 6 * * *' jobs: gen: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: TypeDoc (TypeScript) run: | npm ci npx typedoc --options typedoc.json node scripts/gen-plantuml.ts working-directory: src/typescript - name: JSDoc (JS) run: | npm ci npx jsdoc -d=docs src npx jsdoc-to-plantuml docs/jsdoc.json >docs/class/js.puml working-directory: src/javascript - name: Doxygen (C++) run: | doxygen Doxyfile working-directory: src/cpp - name: SchemaCrawler (DB) run: | java -jar schemacrawler.jar \ --server=postgresql \ --host=$DB_HOST --database=appdb \ --user=app --password=$DB_PWD \ --output-format=puml \ --output-file=docs/db/er.puml - name: Auto-commit run: | git config user.name github-actions[bot] git config user.email 41898282+github-actions[bot]@users.noreply.github.com git add docs/**/*.puml git commit -m "docs: regenerate class diagrams [skip ci]" || exit 0 git push
实战:类图只展示「Public API」 1 2 3 const classes = data.children .filter ((c : any ) => c.flags .isPublic );
避免公开 API 类图被内部字段淹没。
反模式 1. 一张图包含所有内容 1 2 3 const allClasses = data.children .filter ((c : any ) => c.kind === 'class' );
拆 6-8 张主题图(核心 API / 数据模型 / webhooks / 等)。
2. 自动图覆盖手画图 自动生成的图是参考。手画图才是「真正传递意图」的。两者并存。
3. CI 每天生成但没评审 1 2 schedule: - cron: '0 6 * * *'
每天 commit 产生噪声;不开 cron / 不写说明等于 CI 拖累。
4. 自动生成覆盖了定制图 自动图里覆盖你的 nice 图,损失信号。让 CI 把定制图当作可合并资产 ,自动图放在专门的目录(如 docs/auto/)。
评审 checklist 自动生成
手画
一句话总结 从代码到 PlantUML 不必手画一遍。5 个工具(TypeDoc / JSDoc / Doxygen / SchemaCrawler / Python walker) 让 CI 自动出图,配合手画图表达「意图」,文档就长久新鲜。