repowiki-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyzer/api-analyzer.d.ts +22 -0
- package/dist/analyzer/api-analyzer.d.ts.map +1 -0
- package/dist/analyzer/api-analyzer.js +272 -0
- package/dist/analyzer/api-analyzer.js.map +1 -0
- package/dist/analyzer/config-analyzer.d.ts +18 -0
- package/dist/analyzer/config-analyzer.d.ts.map +1 -0
- package/dist/analyzer/config-analyzer.js +200 -0
- package/dist/analyzer/config-analyzer.js.map +1 -0
- package/dist/analyzer/database-analyzer.d.ts +24 -0
- package/dist/analyzer/database-analyzer.d.ts.map +1 -0
- package/dist/analyzer/database-analyzer.js +391 -0
- package/dist/analyzer/database-analyzer.js.map +1 -0
- package/dist/analyzer/index.d.ts +10 -0
- package/dist/analyzer/index.d.ts.map +1 -0
- package/dist/analyzer/index.js +10 -0
- package/dist/analyzer/index.js.map +1 -0
- package/dist/analyzer/module-analyzer.d.ts +20 -0
- package/dist/analyzer/module-analyzer.d.ts.map +1 -0
- package/dist/analyzer/module-analyzer.js +252 -0
- package/dist/analyzer/module-analyzer.js.map +1 -0
- package/dist/analyzer/workflow-analyzer.d.ts +19 -0
- package/dist/analyzer/workflow-analyzer.d.ts.map +1 -0
- package/dist/analyzer/workflow-analyzer.js +165 -0
- package/dist/analyzer/workflow-analyzer.js.map +1 -0
- package/dist/detector/dependency-detector.d.ts +50 -0
- package/dist/detector/dependency-detector.d.ts.map +1 -0
- package/dist/detector/dependency-detector.js +326 -0
- package/dist/detector/dependency-detector.js.map +1 -0
- package/dist/detector/entrypoint-detector.d.ts +30 -0
- package/dist/detector/entrypoint-detector.d.ts.map +1 -0
- package/dist/detector/entrypoint-detector.js +240 -0
- package/dist/detector/entrypoint-detector.js.map +1 -0
- package/dist/detector/index.d.ts +10 -0
- package/dist/detector/index.d.ts.map +1 -0
- package/dist/detector/index.js +10 -0
- package/dist/detector/index.js.map +1 -0
- package/dist/detector/tech-stack-detector.d.ts +41 -0
- package/dist/detector/tech-stack-detector.d.ts.map +1 -0
- package/dist/detector/tech-stack-detector.js +300 -0
- package/dist/detector/tech-stack-detector.js.map +1 -0
- package/dist/generator/index.d.ts +9 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +9 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/markdown-generator.d.ts +71 -0
- package/dist/generator/markdown-generator.d.ts.map +1 -0
- package/dist/generator/markdown-generator.js +235 -0
- package/dist/generator/markdown-generator.js.map +1 -0
- package/dist/generator/mermaid-generator.d.ts +30 -0
- package/dist/generator/mermaid-generator.d.ts.map +1 -0
- package/dist/generator/mermaid-generator.js +297 -0
- package/dist/generator/mermaid-generator.js.map +1 -0
- package/dist/generator/sidebar-generator.d.ts +10 -0
- package/dist/generator/sidebar-generator.d.ts.map +1 -0
- package/dist/generator/sidebar-generator.js +120 -0
- package/dist/generator/sidebar-generator.js.map +1 -0
- package/dist/generator/wiki-generator.d.ts +45 -0
- package/dist/generator/wiki-generator.d.ts.map +1 -0
- package/dist/generator/wiki-generator.js +217 -0
- package/dist/generator/wiki-generator.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/auth-manager.d.ts +50 -0
- package/dist/llm/auth-manager.d.ts.map +1 -0
- package/dist/llm/auth-manager.js +172 -0
- package/dist/llm/auth-manager.js.map +1 -0
- package/dist/llm/index.d.ts +10 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +9 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/llm-client.d.ts +132 -0
- package/dist/llm/llm-client.d.ts.map +1 -0
- package/dist/llm/llm-client.js +308 -0
- package/dist/llm/llm-client.js.map +1 -0
- package/dist/llm/prompt-manager.d.ts +67 -0
- package/dist/llm/prompt-manager.d.ts.map +1 -0
- package/dist/llm/prompt-manager.js +283 -0
- package/dist/llm/prompt-manager.js.map +1 -0
- package/dist/models/analysis-result.d.ts +425 -0
- package/dist/models/analysis-result.d.ts.map +1 -0
- package/dist/models/analysis-result.js +34 -0
- package/dist/models/analysis-result.js.map +1 -0
- package/dist/models/analysis-types.d.ts +223 -0
- package/dist/models/analysis-types.d.ts.map +1 -0
- package/dist/models/analysis-types.js +95 -0
- package/dist/models/analysis-types.js.map +1 -0
- package/dist/models/file-reference.d.ts +62 -0
- package/dist/models/file-reference.d.ts.map +1 -0
- package/dist/models/file-reference.js +34 -0
- package/dist/models/file-reference.js.map +1 -0
- package/dist/models/index.d.ts +10 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +10 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/project-profile.d.ts +48 -0
- package/dist/models/project-profile.d.ts.map +1 -0
- package/dist/models/project-profile.js +26 -0
- package/dist/models/project-profile.js.map +1 -0
- package/dist/models/wiki-page.d.ts +57 -0
- package/dist/models/wiki-page.d.ts.map +1 -0
- package/dist/models/wiki-page.js +19 -0
- package/dist/models/wiki-page.js.map +1 -0
- package/dist/pipeline.d.ts +30 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +159 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/scanner/file-scanner.d.ts +27 -0
- package/dist/scanner/file-scanner.d.ts.map +1 -0
- package/dist/scanner/file-scanner.js +149 -0
- package/dist/scanner/file-scanner.js.map +1 -0
- package/dist/scanner/ignore-rules.d.ts +31 -0
- package/dist/scanner/ignore-rules.d.ts.map +1 -0
- package/dist/scanner/ignore-rules.js +98 -0
- package/dist/scanner/ignore-rules.js.map +1 -0
- package/dist/scanner/index.d.ts +8 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +8 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/tree-builder.d.ts +20 -0
- package/dist/scanner/tree-builder.d.ts.map +1 -0
- package/dist/scanner/tree-builder.js +118 -0
- package/dist/scanner/tree-builder.js.map +1 -0
- package/package.json +34 -0
- package/src/analyzer/api-analyzer.ts +324 -0
- package/src/analyzer/config-analyzer.ts +209 -0
- package/src/analyzer/database-analyzer.ts +468 -0
- package/src/analyzer/index.ts +26 -0
- package/src/analyzer/module-analyzer.ts +308 -0
- package/src/analyzer/workflow-analyzer.ts +190 -0
- package/src/detector/dependency-detector.ts +390 -0
- package/src/detector/entrypoint-detector.ts +270 -0
- package/src/detector/index.ts +21 -0
- package/src/detector/tech-stack-detector.ts +377 -0
- package/src/generator/index.ts +36 -0
- package/src/generator/markdown-generator.ts +277 -0
- package/src/generator/mermaid-generator.ts +340 -0
- package/src/generator/sidebar-generator.ts +134 -0
- package/src/generator/wiki-generator.ts +281 -0
- package/src/index.ts +12 -0
- package/src/llm/auth-manager.ts +207 -0
- package/src/llm/index.ts +21 -0
- package/src/llm/llm-client.ts +417 -0
- package/src/llm/prompt-manager.ts +325 -0
- package/src/models/analysis-result.ts +44 -0
- package/src/models/analysis-types.ts +121 -0
- package/src/models/file-reference.ts +41 -0
- package/src/models/index.ts +44 -0
- package/src/models/project-profile.ts +29 -0
- package/src/models/wiki-page.ts +23 -0
- package/src/pipeline.ts +225 -0
- package/src/scanner/file-scanner.ts +192 -0
- package/src/scanner/ignore-rules.ts +112 -0
- package/src/scanner/index.ts +19 -0
- package/src/scanner/tree-builder.ts +156 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { type ModuleInfo, type ApiRoute, type DatabaseModel } from '../models/index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mermaid 图表生成器
|
|
5
|
+
* 将分析结果转换为 Mermaid 格式的架构图、ER 图和流程图
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 生成项目整体架构的 Mermaid flowchart
|
|
10
|
+
*/
|
|
11
|
+
export function generateArchitectureDiagram(
|
|
12
|
+
modules: ModuleInfo[],
|
|
13
|
+
dependencies: Array<Record<string, unknown>>
|
|
14
|
+
): string {
|
|
15
|
+
const lines: string[] = ['flowchart LR'];
|
|
16
|
+
|
|
17
|
+
// 按类别对模块分组
|
|
18
|
+
const groups = groupModulesByCategory(modules);
|
|
19
|
+
|
|
20
|
+
for (const [category, mods] of Object.entries(groups)) {
|
|
21
|
+
const subgraphId = sanitizeMermaidId(category);
|
|
22
|
+
lines.push(` subgraph ${subgraphId}["${getCategoryLabel(category)}"]`);
|
|
23
|
+
|
|
24
|
+
for (const mod of mods) {
|
|
25
|
+
const nodeId = sanitizeMermaidId(mod.moduleName);
|
|
26
|
+
const label = mod.moduleName;
|
|
27
|
+
lines.push(` ${nodeId}["${label}"]`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
lines.push(' end');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 添加依赖关系边
|
|
34
|
+
const addedEdges = new Set<string>();
|
|
35
|
+
for (const dep of dependencies) {
|
|
36
|
+
const source = dep['source'] as string | undefined;
|
|
37
|
+
const target = dep['target'] as string | undefined;
|
|
38
|
+
if (!source || !target) continue;
|
|
39
|
+
|
|
40
|
+
const sourceModule = findModuleForFile(source, modules);
|
|
41
|
+
const targetModule = findModuleForFile(target, modules);
|
|
42
|
+
if (!sourceModule || !targetModule || sourceModule === targetModule) continue;
|
|
43
|
+
|
|
44
|
+
const edgeKey = `${sourceModule}-->${targetModule}`;
|
|
45
|
+
if (addedEdges.has(edgeKey)) continue;
|
|
46
|
+
addedEdges.add(edgeKey);
|
|
47
|
+
|
|
48
|
+
const srcId = sanitizeMermaidId(sourceModule);
|
|
49
|
+
const tgtId = sanitizeMermaidId(targetModule);
|
|
50
|
+
lines.push(` ${srcId} --> ${tgtId}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return lines.join('\n');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 生成数据库 ER 图的 Mermaid 代码
|
|
58
|
+
*/
|
|
59
|
+
export function generateERDiagram(models: DatabaseModel[]): string {
|
|
60
|
+
if (models.length === 0) return '';
|
|
61
|
+
|
|
62
|
+
const lines: string[] = ['erDiagram'];
|
|
63
|
+
|
|
64
|
+
for (const model of models) {
|
|
65
|
+
lines.push(` ${sanitizeMermaidId(model.name)} {`);
|
|
66
|
+
for (const field of model.fields) {
|
|
67
|
+
const type = sanitizeMermaidType(field.type);
|
|
68
|
+
const nullable = field.nullable ? 'nullable' : 'required';
|
|
69
|
+
lines.push(` ${type} ${field.name} "${nullable}"`);
|
|
70
|
+
}
|
|
71
|
+
lines.push(' }');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 添加关系
|
|
75
|
+
for (const model of models) {
|
|
76
|
+
if (!model.relations) continue;
|
|
77
|
+
for (const rel of model.relations) {
|
|
78
|
+
const srcId = sanitizeMermaidId(model.name);
|
|
79
|
+
const tgtId = sanitizeMermaidId(rel.relatedModel);
|
|
80
|
+
const relSymbol = getERRelationSymbol(rel.relationType);
|
|
81
|
+
const label = rel.fieldName || rel.relationType;
|
|
82
|
+
lines.push(` ${srcId} ${relSymbol} ${tgtId} : "${label}"`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
return lines.join('\n');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 生成模块依赖关系的 Mermaid flowchart
|
|
92
|
+
*/
|
|
93
|
+
export function generateDependencyDiagram(
|
|
94
|
+
modules: ModuleInfo[],
|
|
95
|
+
edges: Array<{ source: string; target: string; isExternal: boolean }>
|
|
96
|
+
): string {
|
|
97
|
+
const lines: string[] = ['flowchart TD'];
|
|
98
|
+
|
|
99
|
+
// 内部模块节点
|
|
100
|
+
lines.push(' subgraph internal["内部模块"]');
|
|
101
|
+
for (const mod of modules) {
|
|
102
|
+
const nodeId = sanitizeMermaidId(mod.moduleName);
|
|
103
|
+
lines.push(` ${nodeId}["${mod.moduleName}"]`);
|
|
104
|
+
}
|
|
105
|
+
lines.push(' end');
|
|
106
|
+
|
|
107
|
+
// 外部依赖
|
|
108
|
+
const externalDeps = new Set<string>();
|
|
109
|
+
for (const edge of edges) {
|
|
110
|
+
if (edge.isExternal) {
|
|
111
|
+
externalDeps.add(edge.target);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (externalDeps.size > 0) {
|
|
116
|
+
lines.push(' subgraph external["外部依赖"]');
|
|
117
|
+
for (const dep of externalDeps) {
|
|
118
|
+
const nodeId = sanitizeMermaidId(`ext_${dep}`);
|
|
119
|
+
lines.push(` ${nodeId}["${dep}"]`);
|
|
120
|
+
}
|
|
121
|
+
lines.push(' end');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 模块间关系
|
|
125
|
+
const addedEdges = new Set<string>();
|
|
126
|
+
for (const edge of edges) {
|
|
127
|
+
const sourceModule = findModuleForFile(edge.source, modules);
|
|
128
|
+
if (!sourceModule) continue;
|
|
129
|
+
|
|
130
|
+
const srcId = sanitizeMermaidId(sourceModule);
|
|
131
|
+
let tgtId: string;
|
|
132
|
+
|
|
133
|
+
if (edge.isExternal) {
|
|
134
|
+
tgtId = sanitizeMermaidId(`ext_${edge.target}`);
|
|
135
|
+
} else {
|
|
136
|
+
const targetModule = findModuleForFile(edge.target, modules);
|
|
137
|
+
if (!targetModule || targetModule === sourceModule) continue;
|
|
138
|
+
tgtId = sanitizeMermaidId(targetModule);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const edgeKey = `${srcId}-->${tgtId}`;
|
|
142
|
+
if (addedEdges.has(edgeKey)) continue;
|
|
143
|
+
addedEdges.add(edgeKey);
|
|
144
|
+
lines.push(` ${srcId} --> ${tgtId}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return lines.join('\n');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* 生成 API 路由的 Mermaid 图
|
|
152
|
+
*/
|
|
153
|
+
export function generateApiDiagram(routes: ApiRoute[]): string {
|
|
154
|
+
if (routes.length === 0) return '';
|
|
155
|
+
|
|
156
|
+
const lines: string[] = ['flowchart LR'];
|
|
157
|
+
|
|
158
|
+
// 按路径前缀分组
|
|
159
|
+
const groups = new Map<string, ApiRoute[]>();
|
|
160
|
+
for (const route of routes) {
|
|
161
|
+
const prefix = route.path.split('/').slice(0, 2).join('/') || '/';
|
|
162
|
+
const group = groups.get(prefix) || [];
|
|
163
|
+
group.push(route);
|
|
164
|
+
groups.set(prefix, group);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
lines.push(' Client["客户端"] --> Router["路由层"]');
|
|
168
|
+
|
|
169
|
+
for (const [prefix, groupRoutes] of groups) {
|
|
170
|
+
const groupId = sanitizeMermaidId(`api_${prefix}`);
|
|
171
|
+
lines.push(` subgraph ${groupId}["${prefix}"]`);
|
|
172
|
+
|
|
173
|
+
for (const route of groupRoutes) {
|
|
174
|
+
const routeId = sanitizeMermaidId(`route_${route.method}_${route.path}`);
|
|
175
|
+
lines.push(` ${routeId}["${route.method.toUpperCase()} ${route.path}"]`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
lines.push(' end');
|
|
179
|
+
lines.push(` Router --> ${groupId}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return lines.join('\n');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 生成简化的技术栈总览图
|
|
187
|
+
*/
|
|
188
|
+
export function generateTechStackDiagram(
|
|
189
|
+
frameworks: string[],
|
|
190
|
+
databases: string[],
|
|
191
|
+
services: string[]
|
|
192
|
+
): string {
|
|
193
|
+
const lines: string[] = ['flowchart TB'];
|
|
194
|
+
|
|
195
|
+
lines.push(' User["用户"] --> Frontend');
|
|
196
|
+
|
|
197
|
+
if (frameworks.length > 0) {
|
|
198
|
+
lines.push(' subgraph Frontend["前端"]');
|
|
199
|
+
for (const fw of frameworks.filter(f =>
|
|
200
|
+
['Next.js', 'React', 'Vue.js', 'Angular', 'Svelte'].includes(f)
|
|
201
|
+
)) {
|
|
202
|
+
lines.push(` ${sanitizeMermaidId(fw)}["${fw}"]`);
|
|
203
|
+
}
|
|
204
|
+
lines.push(' end');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const backendFrameworks = frameworks.filter(f =>
|
|
208
|
+
['FastAPI', 'Express', 'NestJS', 'Django', 'Flask', 'Fastify'].includes(f)
|
|
209
|
+
);
|
|
210
|
+
if (backendFrameworks.length > 0) {
|
|
211
|
+
lines.push(' Frontend --> Backend');
|
|
212
|
+
lines.push(' subgraph Backend["后端"]');
|
|
213
|
+
for (const fw of backendFrameworks) {
|
|
214
|
+
lines.push(` ${sanitizeMermaidId(fw)}["${fw}"]`);
|
|
215
|
+
}
|
|
216
|
+
lines.push(' end');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (databases.length > 0) {
|
|
220
|
+
lines.push(' Backend --> Database');
|
|
221
|
+
lines.push(' subgraph Database["数据存储"]');
|
|
222
|
+
for (const db of databases) {
|
|
223
|
+
lines.push(` ${sanitizeMermaidId(db)}["${db}"]`);
|
|
224
|
+
}
|
|
225
|
+
lines.push(' end');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (services.length > 0) {
|
|
229
|
+
lines.push(' Backend --> Services');
|
|
230
|
+
lines.push(' subgraph Services["外部服务"]');
|
|
231
|
+
for (const svc of services) {
|
|
232
|
+
lines.push(` ${sanitizeMermaidId(svc)}["${svc}"]`);
|
|
233
|
+
}
|
|
234
|
+
lines.push(' end');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return lines.join('\n');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ─── 辅助函数 ─────────────────────────────────────────────────
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 将字符串转换为合法的 Mermaid 节点 ID
|
|
244
|
+
*/
|
|
245
|
+
function sanitizeMermaidId(input: string): string {
|
|
246
|
+
return input
|
|
247
|
+
.replace(/[^a-zA-Z0-9_\u4e00-\u9fff]/g, '_')
|
|
248
|
+
.replace(/^(\d)/, '_$1')
|
|
249
|
+
.replace(/_+/g, '_')
|
|
250
|
+
.replace(/^_|_$/g, '') || 'node';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* 将类型名转换为 Mermaid ER 图兼容的类型
|
|
255
|
+
*/
|
|
256
|
+
function sanitizeMermaidType(type: string): string {
|
|
257
|
+
return type.replace(/[^a-zA-Z0-9]/g, '_') || 'unknown';
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* 获取 ER 关系符号
|
|
262
|
+
*/
|
|
263
|
+
function getERRelationSymbol(relType: string): string {
|
|
264
|
+
const lower = relType.toLowerCase();
|
|
265
|
+
if (lower.includes('manytomany') || lower === 'many-to-many') return '}o--o{';
|
|
266
|
+
if (lower.includes('onetomany') || lower === 'one-to-many') return '||--o{';
|
|
267
|
+
if (lower.includes('manytoone') || lower === 'many-to-one') return '}o--||';
|
|
268
|
+
if (lower.includes('onetoone') || lower === 'one-to-one') return '||--||';
|
|
269
|
+
return '||--o{'; // 默认
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* 按类别对模块分组
|
|
274
|
+
*/
|
|
275
|
+
function groupModulesByCategory(modules: ModuleInfo[]): Record<string, ModuleInfo[]> {
|
|
276
|
+
const groups: Record<string, ModuleInfo[]> = {};
|
|
277
|
+
|
|
278
|
+
for (const mod of modules) {
|
|
279
|
+
const category = inferCategory(mod);
|
|
280
|
+
if (!groups[category]) groups[category] = [];
|
|
281
|
+
groups[category].push(mod);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return groups;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* 推断模块类别
|
|
289
|
+
*/
|
|
290
|
+
function inferCategory(mod: ModuleInfo): string {
|
|
291
|
+
const dir = mod.directory.toLowerCase();
|
|
292
|
+
if (dir.includes('frontend') || dir.includes('web') || dir.includes('client') ||
|
|
293
|
+
dir.includes('app/page') || dir.includes('components')) return 'frontend';
|
|
294
|
+
if (dir.includes('api') || dir.includes('backend') || dir.includes('server')) return 'backend';
|
|
295
|
+
if (dir.includes('agent')) return 'agents';
|
|
296
|
+
if (dir.includes('model') || dir.includes('schema') || dir.includes('entity')) return 'data';
|
|
297
|
+
if (dir.includes('test') || dir.includes('spec')) return 'tests';
|
|
298
|
+
if (dir.includes('config') || dir.includes('infra') || dir.includes('deploy')) return 'infrastructure';
|
|
299
|
+
if (dir.includes('lib') || dir.includes('util') || dir.includes('common') || dir.includes('shared')) return 'shared';
|
|
300
|
+
return 'other';
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* 获取类别的中文标签
|
|
305
|
+
*/
|
|
306
|
+
function getCategoryLabel(category: string): string {
|
|
307
|
+
const labels: Record<string, string> = {
|
|
308
|
+
frontend: '前端应用',
|
|
309
|
+
backend: '后端服务',
|
|
310
|
+
agents: 'Agent 系统',
|
|
311
|
+
data: '数据模型',
|
|
312
|
+
tests: '测试',
|
|
313
|
+
infrastructure: '基础设施',
|
|
314
|
+
shared: '公共模块',
|
|
315
|
+
other: '其他模块'
|
|
316
|
+
};
|
|
317
|
+
return labels[category] || category;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* 根据文件路径找到所属模块
|
|
322
|
+
*/
|
|
323
|
+
function findModuleForFile(filePath: string, modules: ModuleInfo[]): string | null {
|
|
324
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
325
|
+
for (const mod of modules) {
|
|
326
|
+
for (const f of mod.files) {
|
|
327
|
+
if (normalized === f.replace(/\\/g, '/') || normalized.endsWith(f.replace(/\\/g, '/'))) {
|
|
328
|
+
return mod.moduleName;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// 尝试通过目录匹配
|
|
333
|
+
for (const mod of modules) {
|
|
334
|
+
const dir = mod.directory.replace(/\\/g, '/');
|
|
335
|
+
if (normalized.startsWith(dir)) {
|
|
336
|
+
return mod.moduleName;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { WikiPage, ProjectProfile } from '../models/index.js';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 提取页面所在的分组名称(即它所在的相对目录)
|
|
6
|
+
*/
|
|
7
|
+
function getPageGroup(page: WikiPage): string {
|
|
8
|
+
const dir = path.dirname(page.filename);
|
|
9
|
+
if (dir === '.' || dir === '/' || !dir) {
|
|
10
|
+
return '根目录';
|
|
11
|
+
}
|
|
12
|
+
// 返回最上层或次上层的目录名作为分组名
|
|
13
|
+
return dir.replace(/\\/g, '/');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 清理文件名,去除 .md 后缀
|
|
18
|
+
*/
|
|
19
|
+
function stripMarkdownExtension(filePath: string): string {
|
|
20
|
+
if (filePath.endsWith('.md')) {
|
|
21
|
+
return filePath.slice(0, -3);
|
|
22
|
+
}
|
|
23
|
+
return filePath;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 生成符合 GitHub Wiki 扁平或路径兼容的 Wiki 链接
|
|
28
|
+
* 格式:[[显示名称|路径(无后缀)]]
|
|
29
|
+
*/
|
|
30
|
+
function formatWikiLink(page: WikiPage): string {
|
|
31
|
+
const cleanPath = stripMarkdownExtension(page.filename).replace(/\\/g, '/');
|
|
32
|
+
return `[[${page.title}|${cleanPath}]]`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 生成 _Sidebar.md 内容,按目录分组展示
|
|
37
|
+
*/
|
|
38
|
+
export function generateSidebar(pages: WikiPage[]): string {
|
|
39
|
+
const groups: Record<string, WikiPage[]> = {};
|
|
40
|
+
|
|
41
|
+
for (const page of pages) {
|
|
42
|
+
// 跳过 Home.md 和 _Sidebar.md 本身以避免循环链接
|
|
43
|
+
const basename = path.basename(page.filename).toLowerCase();
|
|
44
|
+
if (basename === 'home.md' || basename === '_sidebar.md') {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const group = getPageGroup(page);
|
|
49
|
+
if (!groups[group]) {
|
|
50
|
+
groups[group] = [];
|
|
51
|
+
}
|
|
52
|
+
groups[group].push(page);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const lines: string[] = ['# 项目文档导航', ''];
|
|
56
|
+
|
|
57
|
+
// 定义常见分组的优先级排序,未定义的排在后面
|
|
58
|
+
const groupOrder = [
|
|
59
|
+
'项目概述',
|
|
60
|
+
'架构设计',
|
|
61
|
+
'核心功能模块',
|
|
62
|
+
'前端应用架构',
|
|
63
|
+
'后端服务架构',
|
|
64
|
+
'数据库设计',
|
|
65
|
+
'API 参考文档',
|
|
66
|
+
'AI 集成与提示词设计',
|
|
67
|
+
'安全与认证',
|
|
68
|
+
'开发者指南',
|
|
69
|
+
'部署与运维',
|
|
70
|
+
'故障排除与常见问题',
|
|
71
|
+
'根目录',
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const sortedGroups = Object.keys(groups).sort((a, b) => {
|
|
75
|
+
const indexA = groupOrder.indexOf(a);
|
|
76
|
+
const indexB = groupOrder.indexOf(b);
|
|
77
|
+
if (indexA !== -1 && indexB !== -1) return indexA - indexB;
|
|
78
|
+
if (indexA !== -1) return -1;
|
|
79
|
+
if (indexB !== -1) return 1;
|
|
80
|
+
return a.localeCompare(b);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
for (const group of sortedGroups) {
|
|
84
|
+
const groupPages = groups[group];
|
|
85
|
+
if (group === '根目录') {
|
|
86
|
+
lines.push('### 其他文档');
|
|
87
|
+
} else {
|
|
88
|
+
lines.push(`### ${group}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (const page of groupPages) {
|
|
92
|
+
lines.push(`- ${formatWikiLink(page)}`);
|
|
93
|
+
}
|
|
94
|
+
lines.push('');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return lines.join('\n').trim() + '\n';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 生成 Home.md 内容,为 Wiki 主页提供项目概览及索引导航
|
|
102
|
+
*/
|
|
103
|
+
export function generateHome(pages: WikiPage[], projectProfile: ProjectProfile): string {
|
|
104
|
+
const lines: string[] = [];
|
|
105
|
+
|
|
106
|
+
lines.push(`# 欢迎使用 ${projectProfile.name} 项目 Wiki`);
|
|
107
|
+
lines.push('');
|
|
108
|
+
lines.push('这是一个由 RepoWiki 自动生成的本地代码库知识库。');
|
|
109
|
+
lines.push('');
|
|
110
|
+
|
|
111
|
+
// 项目基本信息卡片
|
|
112
|
+
lines.push('## 项目概要');
|
|
113
|
+
lines.push('');
|
|
114
|
+
lines.push(`- **项目名称**: \`${projectProfile.name}\``);
|
|
115
|
+
lines.push(`- **主力语言**: ${projectProfile.languages.join(', ') || '未知'}`);
|
|
116
|
+
if (projectProfile.frameworks.length > 0) {
|
|
117
|
+
lines.push(`- **使用框架**: ${projectProfile.frameworks.join(', ')}`);
|
|
118
|
+
}
|
|
119
|
+
if (projectProfile.databases.length > 0) {
|
|
120
|
+
lines.push(`- **数据库**: ${projectProfile.databases.join(', ')}`);
|
|
121
|
+
}
|
|
122
|
+
lines.push('');
|
|
123
|
+
|
|
124
|
+
// 全量 Wiki 导航索引
|
|
125
|
+
lines.push('## 知识库目录索引');
|
|
126
|
+
lines.push('');
|
|
127
|
+
|
|
128
|
+
const sidebarContent = generateSidebar(pages);
|
|
129
|
+
// 移除侧边栏标题以融合到 Home.md 中
|
|
130
|
+
const bodyContent = sidebarContent.replace('# 项目文档导航\n\n', '');
|
|
131
|
+
lines.push(bodyContent);
|
|
132
|
+
|
|
133
|
+
return lines.join('\n').trim() + '\n';
|
|
134
|
+
}
|