@girardmedia/bootspring 2.5.0 → 2.5.2
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/README.md +9 -403
- package/bin/bootspring.js +1 -96
- package/dist/cli/index.js +65134 -0
- package/dist/cli-launcher.js +92 -0
- package/dist/core/index.d.ts +2110 -5582
- package/dist/core/index.js +2 -0
- package/dist/core.js +21123 -5413
- package/dist/mcp/index.d.ts +357 -1
- package/dist/mcp/index.js +2 -0
- package/dist/mcp-server.js +51948 -1976
- package/package.json +27 -63
- package/scripts/postinstall.cjs +144 -0
- package/LICENSE +0 -29
- package/dist/cli/index.cjs +0 -20776
- package/generators/api-docs.js +0 -827
- package/generators/decisions.js +0 -655
- package/generators/generate.js +0 -595
- package/generators/health.js +0 -942
- package/generators/index.ts +0 -82
- package/generators/presets/full.js +0 -28
- package/generators/presets/index.js +0 -12
- package/generators/presets/minimal.js +0 -29
- package/generators/presets/standard.js +0 -28
- package/generators/questionnaire.js +0 -414
- package/generators/sections/advanced.js +0 -136
- package/generators/sections/ai.js +0 -106
- package/generators/sections/auth.js +0 -89
- package/generators/sections/backend.js +0 -146
- package/generators/sections/business.js +0 -118
- package/generators/sections/content.js +0 -300
- package/generators/sections/deployment.js +0 -139
- package/generators/sections/features.js +0 -122
- package/generators/sections/frontend.js +0 -118
- package/generators/sections/identity.js +0 -76
- package/generators/sections/index.js +0 -40
- package/generators/sections/instructions.js +0 -146
- package/generators/sections/payments.js +0 -104
- package/generators/sections/plugins.js +0 -142
- package/generators/sections/pre-build.js +0 -130
- package/generators/sections/security.js +0 -127
- package/generators/sections/technical.js +0 -171
- package/generators/sections/testing.js +0 -125
- package/generators/sections/workflow.js +0 -104
- package/generators/sprint.js +0 -675
- package/generators/templates/agents.template.js +0 -199
- package/generators/templates/assistant-context.template.js +0 -83
- package/generators/templates/build-planning.template.js +0 -708
- package/generators/templates/claude.template.js +0 -379
- package/generators/templates/content.template.js +0 -819
- package/generators/templates/index.js +0 -16
- package/generators/templates/planning.template.js +0 -515
- package/generators/templates/seed.template.js +0 -109
- package/generators/visual-doc-generator.js +0 -910
- package/scripts/postinstall.js +0 -197
- /package/{claude-commands → assets/claude-commands}/agent.md +0 -0
- /package/{claude-commands → assets/claude-commands}/bs.md +0 -0
- /package/{claude-commands → assets/claude-commands}/build.md +0 -0
- /package/{claude-commands → assets/claude-commands}/skill.md +0 -0
- /package/{claude-commands → assets/claude-commands}/todo.md +0 -0
|
@@ -1,910 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bootspring Visual Document Generator
|
|
3
|
-
*
|
|
4
|
-
* Generates visual documentation using Mermaid diagrams.
|
|
5
|
-
* Creates architecture, data flow, dependency, and component diagrams.
|
|
6
|
-
*
|
|
7
|
-
* @package bootspring
|
|
8
|
-
* @module generators/visual-doc-generator
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const path = require('path');
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Diagram types supported by the generator
|
|
16
|
-
*/
|
|
17
|
-
const DIAGRAM_TYPES = {
|
|
18
|
-
architecture: 'flowchart',
|
|
19
|
-
dataFlow: 'flowchart',
|
|
20
|
-
dependencies: 'flowchart',
|
|
21
|
-
components: 'flowchart',
|
|
22
|
-
sequence: 'sequenceDiagram',
|
|
23
|
-
state: 'stateDiagram-v2',
|
|
24
|
-
entityRelation: 'erDiagram',
|
|
25
|
-
classHierarchy: 'classDiagram',
|
|
26
|
-
mindmap: 'mindmap',
|
|
27
|
-
timeline: 'timeline'
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Default colors for diagram elements
|
|
32
|
-
*/
|
|
33
|
-
const DIAGRAM_COLORS = {
|
|
34
|
-
primary: '#4A90D9',
|
|
35
|
-
secondary: '#50B83C',
|
|
36
|
-
accent: '#F49342',
|
|
37
|
-
warning: '#FFCC00',
|
|
38
|
-
error: '#DE3618',
|
|
39
|
-
neutral: '#637381',
|
|
40
|
-
background: '#F4F6F8'
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Layer styling for architecture diagrams
|
|
45
|
-
*/
|
|
46
|
-
const LAYER_STYLES = {
|
|
47
|
-
presentation: { fill: '#E3F2FD', stroke: '#1976D2' },
|
|
48
|
-
api: { fill: '#E8F5E9', stroke: '#388E3C' },
|
|
49
|
-
service: { fill: '#FFF3E0', stroke: '#F57C00' },
|
|
50
|
-
domain: { fill: '#FCE4EC', stroke: '#C2185B' },
|
|
51
|
-
data: { fill: '#F3E5F5', stroke: '#7B1FA2' },
|
|
52
|
-
infrastructure: { fill: '#ECEFF1', stroke: '#546E7A' }
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Visual Document Generator
|
|
57
|
-
* Creates Mermaid diagrams from codebase analysis
|
|
58
|
-
*/
|
|
59
|
-
class VisualDocGenerator {
|
|
60
|
-
/**
|
|
61
|
-
* Create a new VisualDocGenerator
|
|
62
|
-
* @param {object} options - Configuration options
|
|
63
|
-
* @param {string} options.projectRoot - Project root directory
|
|
64
|
-
* @param {string} options.outputDir - Output directory for diagrams
|
|
65
|
-
* @param {string} options.theme - Mermaid theme (default, forest, dark, neutral)
|
|
66
|
-
*/
|
|
67
|
-
constructor(options = {}) {
|
|
68
|
-
this.projectRoot = options.projectRoot || process.cwd();
|
|
69
|
-
this.outputDir = options.outputDir || path.join(this.projectRoot, 'docs', 'diagrams');
|
|
70
|
-
this.theme = options.theme || 'default';
|
|
71
|
-
this.includeStyles = options.includeStyles !== false;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Generate architecture diagram from analysis
|
|
76
|
-
* @param {object} analysis - Architecture analysis data
|
|
77
|
-
* @returns {string} Mermaid diagram code
|
|
78
|
-
*/
|
|
79
|
-
generateArchitectureDiagram(analysis) {
|
|
80
|
-
const lines = ['flowchart TB'];
|
|
81
|
-
|
|
82
|
-
// Add theme directive
|
|
83
|
-
lines.unshift(`%%{init: {'theme': '${this.theme}'}}%%`);
|
|
84
|
-
|
|
85
|
-
// Add subgraphs for each layer
|
|
86
|
-
const layers = analysis.layers || this.detectLayers(analysis);
|
|
87
|
-
|
|
88
|
-
for (const [layerName, components] of Object.entries(layers)) {
|
|
89
|
-
if (components && components.length > 0) {
|
|
90
|
-
const style = LAYER_STYLES[layerName] || LAYER_STYLES.neutral;
|
|
91
|
-
lines.push(` subgraph ${this.sanitizeId(layerName)}["${this.formatLayerName(layerName)}"]`);
|
|
92
|
-
|
|
93
|
-
for (const component of components) {
|
|
94
|
-
const nodeId = this.sanitizeId(component.name || component);
|
|
95
|
-
const nodeName = component.name || component;
|
|
96
|
-
lines.push(` ${nodeId}["${nodeName}"]`);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
lines.push(' end');
|
|
100
|
-
lines.push('');
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Add connections between layers
|
|
105
|
-
const connections = analysis.connections || this.inferConnections(layers);
|
|
106
|
-
for (const conn of connections) {
|
|
107
|
-
const fromId = this.sanitizeId(conn.from);
|
|
108
|
-
const toId = this.sanitizeId(conn.to);
|
|
109
|
-
const label = conn.label ? `|"${conn.label}"|` : '';
|
|
110
|
-
lines.push(` ${fromId} -->${label} ${toId}`);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Add styling
|
|
114
|
-
if (this.includeStyles) {
|
|
115
|
-
lines.push('');
|
|
116
|
-
for (const [layerName, style] of Object.entries(LAYER_STYLES)) {
|
|
117
|
-
const layerId = this.sanitizeId(layerName);
|
|
118
|
-
lines.push(` style ${layerId} fill:${style.fill},stroke:${style.stroke}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return lines.join('\n');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Generate data flow diagram
|
|
127
|
-
* @param {object} analysis - Data flow analysis
|
|
128
|
-
* @returns {string} Mermaid diagram code
|
|
129
|
-
*/
|
|
130
|
-
generateDataFlowDiagram(analysis) {
|
|
131
|
-
const lines = [
|
|
132
|
-
`%%{init: {'theme': '${this.theme}'}}%%`,
|
|
133
|
-
'flowchart LR'
|
|
134
|
-
];
|
|
135
|
-
|
|
136
|
-
// Add data sources
|
|
137
|
-
if (analysis.sources) {
|
|
138
|
-
lines.push(' subgraph Sources["Data Sources"]');
|
|
139
|
-
for (const source of analysis.sources) {
|
|
140
|
-
const id = this.sanitizeId(source.name);
|
|
141
|
-
lines.push(` ${id}[("${source.name}")]`);
|
|
142
|
-
}
|
|
143
|
-
lines.push(' end');
|
|
144
|
-
lines.push('');
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Add processors
|
|
148
|
-
if (analysis.processors) {
|
|
149
|
-
lines.push(' subgraph Processors["Data Processors"]');
|
|
150
|
-
for (const proc of analysis.processors) {
|
|
151
|
-
const id = this.sanitizeId(proc.name);
|
|
152
|
-
lines.push(` ${id}["${proc.name}"]`);
|
|
153
|
-
}
|
|
154
|
-
lines.push(' end');
|
|
155
|
-
lines.push('');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Add outputs
|
|
159
|
-
if (analysis.outputs) {
|
|
160
|
-
lines.push(' subgraph Outputs["Data Outputs"]');
|
|
161
|
-
for (const output of analysis.outputs) {
|
|
162
|
-
const id = this.sanitizeId(output.name);
|
|
163
|
-
const shape = output.type === 'api' ? `{{"${output.name}"}}` : `>"${output.name}"]`;
|
|
164
|
-
lines.push(` ${id}${shape}`);
|
|
165
|
-
}
|
|
166
|
-
lines.push(' end');
|
|
167
|
-
lines.push('');
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Add flows
|
|
171
|
-
if (analysis.flows) {
|
|
172
|
-
for (const flow of analysis.flows) {
|
|
173
|
-
const fromId = this.sanitizeId(flow.from);
|
|
174
|
-
const toId = this.sanitizeId(flow.to);
|
|
175
|
-
const arrow = flow.async ? '-.->' : '-->';
|
|
176
|
-
const label = flow.data ? `|"${flow.data}"|` : '';
|
|
177
|
-
lines.push(` ${fromId} ${arrow}${label} ${toId}`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return lines.join('\n');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Generate dependency diagram
|
|
186
|
-
* @param {object} analysis - Dependency analysis
|
|
187
|
-
* @returns {string} Mermaid diagram code
|
|
188
|
-
*/
|
|
189
|
-
generateDependencyDiagram(analysis) {
|
|
190
|
-
const lines = [
|
|
191
|
-
`%%{init: {'theme': '${this.theme}'}}%%`,
|
|
192
|
-
'flowchart TD'
|
|
193
|
-
];
|
|
194
|
-
|
|
195
|
-
// Group modules by type
|
|
196
|
-
const moduleGroups = this.groupModulesByType(analysis.modules || []);
|
|
197
|
-
|
|
198
|
-
for (const [groupName, modules] of Object.entries(moduleGroups)) {
|
|
199
|
-
if (modules.length > 0) {
|
|
200
|
-
lines.push(` subgraph ${this.sanitizeId(groupName)}["${groupName}"]`);
|
|
201
|
-
|
|
202
|
-
for (const mod of modules) {
|
|
203
|
-
const id = this.sanitizeId(mod.name);
|
|
204
|
-
const shape = mod.external ? `(("${mod.name}"))` : `["${mod.name}"]`;
|
|
205
|
-
lines.push(` ${id}${shape}`);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
lines.push(' end');
|
|
209
|
-
lines.push('');
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Add dependencies
|
|
214
|
-
if (analysis.dependencies) {
|
|
215
|
-
for (const dep of analysis.dependencies) {
|
|
216
|
-
const fromId = this.sanitizeId(dep.from);
|
|
217
|
-
const toId = this.sanitizeId(dep.to);
|
|
218
|
-
const style = dep.devOnly ? '-.->' : '-->';
|
|
219
|
-
lines.push(` ${fromId} ${style} ${toId}`);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Highlight circular dependencies
|
|
224
|
-
if (analysis.circular && analysis.circular.length > 0) {
|
|
225
|
-
lines.push('');
|
|
226
|
-
lines.push(' %% Circular Dependencies (highlighted in red)');
|
|
227
|
-
for (const circular of analysis.circular) {
|
|
228
|
-
const id1 = this.sanitizeId(circular[0]);
|
|
229
|
-
const id2 = this.sanitizeId(circular[1]);
|
|
230
|
-
lines.push(` ${id1} <--> ${id2}`);
|
|
231
|
-
lines.push(` linkStyle ${lines.length - 2} stroke:red,stroke-width:2px`);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
return lines.join('\n');
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Generate component diagram
|
|
240
|
-
* @param {object} analysis - Component analysis
|
|
241
|
-
* @returns {string} Mermaid diagram code
|
|
242
|
-
*/
|
|
243
|
-
generateComponentDiagram(analysis) {
|
|
244
|
-
const lines = [
|
|
245
|
-
`%%{init: {'theme': '${this.theme}'}}%%`,
|
|
246
|
-
'flowchart TB'
|
|
247
|
-
];
|
|
248
|
-
|
|
249
|
-
// Add components by category
|
|
250
|
-
const categories = analysis.categories || { components: analysis.components || [] };
|
|
251
|
-
|
|
252
|
-
for (const [category, components] of Object.entries(categories)) {
|
|
253
|
-
if (components && components.length > 0) {
|
|
254
|
-
lines.push(` subgraph ${this.sanitizeId(category)}["${this.formatCategoryName(category)}"]`);
|
|
255
|
-
|
|
256
|
-
for (const comp of components) {
|
|
257
|
-
const id = this.sanitizeId(comp.name);
|
|
258
|
-
const props = comp.props ? ` (${comp.props.join(', ')})` : '';
|
|
259
|
-
lines.push(` ${id}["${comp.name}${props}"]`);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
lines.push(' end');
|
|
263
|
-
lines.push('');
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Add component relationships
|
|
268
|
-
if (analysis.relationships) {
|
|
269
|
-
for (const rel of analysis.relationships) {
|
|
270
|
-
const parentId = this.sanitizeId(rel.parent);
|
|
271
|
-
const childId = this.sanitizeId(rel.child);
|
|
272
|
-
const label = rel.type ? `|"${rel.type}"|` : '';
|
|
273
|
-
lines.push(` ${parentId} -->${label} ${childId}`);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return lines.join('\n');
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Generate sequence diagram
|
|
282
|
-
* @param {object} analysis - Sequence analysis
|
|
283
|
-
* @returns {string} Mermaid diagram code
|
|
284
|
-
*/
|
|
285
|
-
generateSequenceDiagram(analysis) {
|
|
286
|
-
const lines = [
|
|
287
|
-
`%%{init: {'theme': '${this.theme}'}}%%`,
|
|
288
|
-
'sequenceDiagram'
|
|
289
|
-
];
|
|
290
|
-
|
|
291
|
-
// Add participants
|
|
292
|
-
if (analysis.participants) {
|
|
293
|
-
for (const participant of analysis.participants) {
|
|
294
|
-
const type = participant.type === 'actor' ? 'actor' : 'participant';
|
|
295
|
-
lines.push(` ${type} ${participant.name}`);
|
|
296
|
-
}
|
|
297
|
-
lines.push('');
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Add messages
|
|
301
|
-
if (analysis.messages) {
|
|
302
|
-
for (const msg of analysis.messages) {
|
|
303
|
-
const arrow = this.getSequenceArrow(msg.type);
|
|
304
|
-
lines.push(` ${msg.from}${arrow}${msg.to}: ${msg.message}`);
|
|
305
|
-
|
|
306
|
-
// Add notes if present
|
|
307
|
-
if (msg.note) {
|
|
308
|
-
lines.push(` Note over ${msg.from},${msg.to}: ${msg.note}`);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Add loops/conditions
|
|
314
|
-
if (analysis.blocks) {
|
|
315
|
-
for (const block of analysis.blocks) {
|
|
316
|
-
if (block.type === 'loop') {
|
|
317
|
-
lines.push(` loop ${block.condition}`);
|
|
318
|
-
} else if (block.type === 'alt') {
|
|
319
|
-
lines.push(` alt ${block.condition}`);
|
|
320
|
-
} else if (block.type === 'opt') {
|
|
321
|
-
lines.push(` opt ${block.condition}`);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
for (const msg of block.messages || []) {
|
|
325
|
-
const arrow = this.getSequenceArrow(msg.type);
|
|
326
|
-
lines.push(` ${msg.from}${arrow}${msg.to}: ${msg.message}`);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
lines.push(' end');
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
return lines.join('\n');
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Generate entity-relationship diagram
|
|
338
|
-
* @param {object} analysis - Entity analysis (database schema)
|
|
339
|
-
* @returns {string} Mermaid diagram code
|
|
340
|
-
*/
|
|
341
|
-
generateERDiagram(analysis) {
|
|
342
|
-
const lines = [
|
|
343
|
-
`%%{init: {'theme': '${this.theme}'}}%%`,
|
|
344
|
-
'erDiagram'
|
|
345
|
-
];
|
|
346
|
-
|
|
347
|
-
// Add entities
|
|
348
|
-
if (analysis.entities) {
|
|
349
|
-
for (const entity of analysis.entities) {
|
|
350
|
-
lines.push(` ${entity.name} {`);
|
|
351
|
-
|
|
352
|
-
for (const field of entity.fields || []) {
|
|
353
|
-
const pk = field.primaryKey ? ' PK' : '';
|
|
354
|
-
const fk = field.foreignKey ? ' FK' : '';
|
|
355
|
-
lines.push(` ${field.type} ${field.name}${pk}${fk}`);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
lines.push(' }');
|
|
359
|
-
lines.push('');
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Add relationships
|
|
364
|
-
if (analysis.relationships) {
|
|
365
|
-
for (const rel of analysis.relationships) {
|
|
366
|
-
const cardinality = this.getERCardinality(rel.type);
|
|
367
|
-
const label = rel.label ? ` : "${rel.label}"` : '';
|
|
368
|
-
lines.push(` ${rel.from} ${cardinality} ${rel.to}${label}`);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
return lines.join('\n');
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Generate class diagram
|
|
377
|
-
* @param {object} analysis - Class hierarchy analysis
|
|
378
|
-
* @returns {string} Mermaid diagram code
|
|
379
|
-
*/
|
|
380
|
-
generateClassDiagram(analysis) {
|
|
381
|
-
const lines = [
|
|
382
|
-
`%%{init: {'theme': '${this.theme}'}}%%`,
|
|
383
|
-
'classDiagram'
|
|
384
|
-
];
|
|
385
|
-
|
|
386
|
-
// Add classes
|
|
387
|
-
if (analysis.classes) {
|
|
388
|
-
for (const cls of analysis.classes) {
|
|
389
|
-
lines.push(` class ${cls.name} {`);
|
|
390
|
-
|
|
391
|
-
// Add attributes
|
|
392
|
-
for (const attr of cls.attributes || []) {
|
|
393
|
-
const visibility = this.getVisibilitySymbol(attr.visibility);
|
|
394
|
-
lines.push(` ${visibility}${attr.type} ${attr.name}`);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// Add methods
|
|
398
|
-
for (const method of cls.methods || []) {
|
|
399
|
-
const visibility = this.getVisibilitySymbol(method.visibility);
|
|
400
|
-
const params = method.params ? method.params.join(', ') : '';
|
|
401
|
-
const returnType = method.returns ? ` ${method.returns}` : '';
|
|
402
|
-
lines.push(` ${visibility}${method.name}(${params})${returnType}`);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
lines.push(' }');
|
|
406
|
-
lines.push('');
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Add relationships
|
|
411
|
-
if (analysis.relationships) {
|
|
412
|
-
for (const rel of analysis.relationships) {
|
|
413
|
-
const arrow = this.getClassRelationArrow(rel.type);
|
|
414
|
-
const label = rel.label ? ` : ${rel.label}` : '';
|
|
415
|
-
lines.push(` ${rel.from} ${arrow} ${rel.to}${label}`);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// Add notes/annotations
|
|
420
|
-
if (analysis.notes) {
|
|
421
|
-
for (const note of analysis.notes) {
|
|
422
|
-
lines.push(` note for ${note.class} "${note.text}"`);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
return lines.join('\n');
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Generate state diagram
|
|
431
|
-
* @param {object} analysis - State machine analysis
|
|
432
|
-
* @returns {string} Mermaid diagram code
|
|
433
|
-
*/
|
|
434
|
-
generateStateDiagram(analysis) {
|
|
435
|
-
const lines = [
|
|
436
|
-
`%%{init: {'theme': '${this.theme}'}}%%`,
|
|
437
|
-
'stateDiagram-v2'
|
|
438
|
-
];
|
|
439
|
-
|
|
440
|
-
// Add states
|
|
441
|
-
if (analysis.states) {
|
|
442
|
-
for (const state of analysis.states) {
|
|
443
|
-
if (state.type === 'composite') {
|
|
444
|
-
lines.push(` state ${state.name} {`);
|
|
445
|
-
for (const substate of state.substates || []) {
|
|
446
|
-
lines.push(` ${substate}`);
|
|
447
|
-
}
|
|
448
|
-
lines.push(' }');
|
|
449
|
-
} else if (state.description) {
|
|
450
|
-
lines.push(` state "${state.description}" as ${state.name}`);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
lines.push('');
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// Add transitions
|
|
457
|
-
if (analysis.transitions) {
|
|
458
|
-
for (const trans of analysis.transitions) {
|
|
459
|
-
const from = trans.from === 'initial' ? '[*]' : trans.from;
|
|
460
|
-
const to = trans.to === 'final' ? '[*]' : trans.to;
|
|
461
|
-
const label = trans.event ? ` : ${trans.event}` : '';
|
|
462
|
-
lines.push(` ${from} --> ${to}${label}`);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
return lines.join('\n');
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* Generate mindmap diagram
|
|
471
|
-
* @param {object} analysis - Hierarchical concept analysis
|
|
472
|
-
* @returns {string} Mermaid diagram code
|
|
473
|
-
*/
|
|
474
|
-
generateMindmapDiagram(analysis) {
|
|
475
|
-
const lines = [
|
|
476
|
-
`%%{init: {'theme': '${this.theme}'}}%%`,
|
|
477
|
-
'mindmap'
|
|
478
|
-
];
|
|
479
|
-
|
|
480
|
-
// Build tree structure
|
|
481
|
-
if (analysis.root) {
|
|
482
|
-
this.addMindmapNode(lines, analysis.root, 2);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
return lines.join('\n');
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* Add mindmap node recursively
|
|
490
|
-
* @param {Array} lines - Output lines
|
|
491
|
-
* @param {object} node - Node to add
|
|
492
|
-
* @param {number} indent - Indentation level
|
|
493
|
-
*/
|
|
494
|
-
addMindmapNode(lines, node, indent) {
|
|
495
|
-
const spaces = ' '.repeat(indent);
|
|
496
|
-
const shape = node.shape || '';
|
|
497
|
-
const text = node.name || node;
|
|
498
|
-
|
|
499
|
-
if (typeof text === 'string') {
|
|
500
|
-
lines.push(`${spaces}${shape}${text}`);
|
|
501
|
-
} else {
|
|
502
|
-
lines.push(`${spaces}${shape}${text.name}`);
|
|
503
|
-
|
|
504
|
-
if (text.children) {
|
|
505
|
-
for (const child of text.children) {
|
|
506
|
-
this.addMindmapNode(lines, child, indent + 2);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
/**
|
|
513
|
-
* Generate timeline diagram
|
|
514
|
-
* @param {object} analysis - Timeline data
|
|
515
|
-
* @returns {string} Mermaid diagram code
|
|
516
|
-
*/
|
|
517
|
-
generateTimelineDiagram(analysis) {
|
|
518
|
-
const lines = [
|
|
519
|
-
`%%{init: {'theme': '${this.theme}'}}%%`,
|
|
520
|
-
'timeline'
|
|
521
|
-
];
|
|
522
|
-
|
|
523
|
-
// Add title
|
|
524
|
-
if (analysis.title) {
|
|
525
|
-
lines.push(` title ${analysis.title}`);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// Add sections/periods
|
|
529
|
-
if (analysis.periods) {
|
|
530
|
-
for (const period of analysis.periods) {
|
|
531
|
-
lines.push(` section ${period.name}`);
|
|
532
|
-
|
|
533
|
-
for (const event of period.events || []) {
|
|
534
|
-
lines.push(` ${event.date} : ${event.description}`);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
return lines.join('\n');
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* Generate all diagrams from comprehensive analysis
|
|
544
|
-
* @param {object} analysis - Full codebase analysis
|
|
545
|
-
* @returns {object} Generated diagrams by type
|
|
546
|
-
*/
|
|
547
|
-
generateAllDiagrams(analysis) {
|
|
548
|
-
const diagrams = {};
|
|
549
|
-
|
|
550
|
-
// Architecture diagram
|
|
551
|
-
if (analysis.architecture) {
|
|
552
|
-
diagrams.architecture = {
|
|
553
|
-
type: 'architecture',
|
|
554
|
-
title: 'System Architecture',
|
|
555
|
-
code: this.generateArchitectureDiagram(analysis.architecture),
|
|
556
|
-
filename: 'architecture.mmd'
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// Data flow diagram
|
|
561
|
-
if (analysis.dataFlow) {
|
|
562
|
-
diagrams.dataFlow = {
|
|
563
|
-
type: 'dataFlow',
|
|
564
|
-
title: 'Data Flow',
|
|
565
|
-
code: this.generateDataFlowDiagram(analysis.dataFlow),
|
|
566
|
-
filename: 'data-flow.mmd'
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// Dependency diagram
|
|
571
|
-
if (analysis.dependencies) {
|
|
572
|
-
diagrams.dependencies = {
|
|
573
|
-
type: 'dependencies',
|
|
574
|
-
title: 'Module Dependencies',
|
|
575
|
-
code: this.generateDependencyDiagram(analysis.dependencies),
|
|
576
|
-
filename: 'dependencies.mmd'
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// Component diagram
|
|
581
|
-
if (analysis.components) {
|
|
582
|
-
diagrams.components = {
|
|
583
|
-
type: 'components',
|
|
584
|
-
title: 'Component Hierarchy',
|
|
585
|
-
code: this.generateComponentDiagram(analysis.components),
|
|
586
|
-
filename: 'components.mmd'
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// ER diagram (if database schema present)
|
|
591
|
-
if (analysis.database) {
|
|
592
|
-
diagrams.database = {
|
|
593
|
-
type: 'entityRelation',
|
|
594
|
-
title: 'Database Schema',
|
|
595
|
-
code: this.generateERDiagram(analysis.database),
|
|
596
|
-
filename: 'database.mmd'
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// Class diagram (if class hierarchy present)
|
|
601
|
-
if (analysis.classes) {
|
|
602
|
-
diagrams.classes = {
|
|
603
|
-
type: 'classHierarchy',
|
|
604
|
-
title: 'Class Hierarchy',
|
|
605
|
-
code: this.generateClassDiagram(analysis.classes),
|
|
606
|
-
filename: 'classes.mmd'
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
return diagrams;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* Save diagrams to files
|
|
615
|
-
* @param {object} diagrams - Generated diagrams
|
|
616
|
-
* @returns {Array<object>} Saved file information
|
|
617
|
-
*/
|
|
618
|
-
saveDiagrams(diagrams) {
|
|
619
|
-
// Ensure output directory exists
|
|
620
|
-
if (!fs.existsSync(this.outputDir)) {
|
|
621
|
-
fs.mkdirSync(this.outputDir, { recursive: true });
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
const saved = [];
|
|
625
|
-
|
|
626
|
-
for (const [type, diagram] of Object.entries(diagrams)) {
|
|
627
|
-
const filepath = path.join(this.outputDir, diagram.filename);
|
|
628
|
-
|
|
629
|
-
// Add header comment
|
|
630
|
-
const content = [
|
|
631
|
-
`%% ${diagram.title}`,
|
|
632
|
-
'%% Generated by Bootspring Visual Doc Generator',
|
|
633
|
-
`%% ${new Date().toISOString()}`,
|
|
634
|
-
'',
|
|
635
|
-
diagram.code
|
|
636
|
-
].join('\n');
|
|
637
|
-
|
|
638
|
-
fs.writeFileSync(filepath, content, 'utf8');
|
|
639
|
-
|
|
640
|
-
saved.push({
|
|
641
|
-
type,
|
|
642
|
-
title: diagram.title,
|
|
643
|
-
filepath,
|
|
644
|
-
filename: diagram.filename,
|
|
645
|
-
size: content.length
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
return saved;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
/**
|
|
653
|
-
* Generate markdown documentation with embedded diagrams
|
|
654
|
-
* @param {object} diagrams - Generated diagrams
|
|
655
|
-
* @param {object} options - Documentation options
|
|
656
|
-
* @returns {string} Markdown documentation
|
|
657
|
-
*/
|
|
658
|
-
generateMarkdownDoc(diagrams, options = {}) {
|
|
659
|
-
const title = options.title || 'Visual Documentation';
|
|
660
|
-
const lines = [
|
|
661
|
-
`# ${title}`,
|
|
662
|
-
'',
|
|
663
|
-
`Generated: ${new Date().toISOString()}`,
|
|
664
|
-
'',
|
|
665
|
-
'---',
|
|
666
|
-
''
|
|
667
|
-
];
|
|
668
|
-
|
|
669
|
-
for (const [type, diagram] of Object.entries(diagrams)) {
|
|
670
|
-
lines.push(`## ${diagram.title}`);
|
|
671
|
-
lines.push('');
|
|
672
|
-
lines.push('```mermaid');
|
|
673
|
-
lines.push(diagram.code);
|
|
674
|
-
lines.push('```');
|
|
675
|
-
lines.push('');
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
return lines.join('\n');
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
// === Helper Methods ===
|
|
682
|
-
|
|
683
|
-
/**
|
|
684
|
-
* Sanitize string for use as Mermaid node ID
|
|
685
|
-
* @param {string} str - String to sanitize
|
|
686
|
-
* @returns {string} Sanitized ID
|
|
687
|
-
*/
|
|
688
|
-
sanitizeId(str) {
|
|
689
|
-
return str
|
|
690
|
-
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
691
|
-
.replace(/^_+|_+$/g, '')
|
|
692
|
-
.replace(/_+/g, '_');
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
/**
|
|
696
|
-
* Format layer name for display
|
|
697
|
-
* @param {string} name - Layer name
|
|
698
|
-
* @returns {string} Formatted name
|
|
699
|
-
*/
|
|
700
|
-
formatLayerName(name) {
|
|
701
|
-
return name.charAt(0).toUpperCase() + name.slice(1) + ' Layer';
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
/**
|
|
705
|
-
* Format category name for display
|
|
706
|
-
* @param {string} name - Category name
|
|
707
|
-
* @returns {string} Formatted name
|
|
708
|
-
*/
|
|
709
|
-
formatCategoryName(name) {
|
|
710
|
-
return name
|
|
711
|
-
.split(/[-_]/)
|
|
712
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
713
|
-
.join(' ');
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
/**
|
|
717
|
-
* Detect layers from analysis
|
|
718
|
-
* @param {object} analysis - Analysis data
|
|
719
|
-
* @returns {object} Detected layers
|
|
720
|
-
*/
|
|
721
|
-
detectLayers(analysis) {
|
|
722
|
-
const layers = {
|
|
723
|
-
presentation: [],
|
|
724
|
-
api: [],
|
|
725
|
-
service: [],
|
|
726
|
-
data: []
|
|
727
|
-
};
|
|
728
|
-
|
|
729
|
-
if (analysis.files) {
|
|
730
|
-
for (const file of analysis.files) {
|
|
731
|
-
const name = path.basename(file, path.extname(file));
|
|
732
|
-
|
|
733
|
-
if (/component|page|view|ui/i.test(file)) {
|
|
734
|
-
layers.presentation.push({ name });
|
|
735
|
-
} else if (/api|route|endpoint|controller/i.test(file)) {
|
|
736
|
-
layers.api.push({ name });
|
|
737
|
-
} else if (/service|handler|manager/i.test(file)) {
|
|
738
|
-
layers.service.push({ name });
|
|
739
|
-
} else if (/model|schema|repository|database/i.test(file)) {
|
|
740
|
-
layers.data.push({ name });
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
return layers;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
/**
|
|
749
|
-
* Infer connections between layers
|
|
750
|
-
* @param {object} layers - Layer data
|
|
751
|
-
* @returns {Array<object>} Inferred connections
|
|
752
|
-
*/
|
|
753
|
-
inferConnections(layers) {
|
|
754
|
-
const connections = [];
|
|
755
|
-
const layerOrder = ['presentation', 'api', 'service', 'data'];
|
|
756
|
-
|
|
757
|
-
for (let i = 0; i < layerOrder.length - 1; i++) {
|
|
758
|
-
const currentLayer = layerOrder[i];
|
|
759
|
-
const nextLayer = layerOrder[i + 1];
|
|
760
|
-
|
|
761
|
-
if (layers[currentLayer]?.length > 0 && layers[nextLayer]?.length > 0) {
|
|
762
|
-
connections.push({
|
|
763
|
-
from: this.sanitizeId(currentLayer),
|
|
764
|
-
to: this.sanitizeId(nextLayer)
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
return connections;
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
/**
|
|
773
|
-
* Group modules by type
|
|
774
|
-
* @param {Array} modules - Module list
|
|
775
|
-
* @returns {object} Grouped modules
|
|
776
|
-
*/
|
|
777
|
-
groupModulesByType(modules) {
|
|
778
|
-
const groups = {
|
|
779
|
-
'Core Modules': [],
|
|
780
|
-
'External Dependencies': [],
|
|
781
|
-
'Dev Dependencies': [],
|
|
782
|
-
'Utilities': []
|
|
783
|
-
};
|
|
784
|
-
|
|
785
|
-
for (const mod of modules) {
|
|
786
|
-
if (mod.external) {
|
|
787
|
-
if (mod.devOnly) {
|
|
788
|
-
groups['Dev Dependencies'].push(mod);
|
|
789
|
-
} else {
|
|
790
|
-
groups['External Dependencies'].push(mod);
|
|
791
|
-
}
|
|
792
|
-
} else if (/util|helper|lib/i.test(mod.name)) {
|
|
793
|
-
groups['Utilities'].push(mod);
|
|
794
|
-
} else {
|
|
795
|
-
groups['Core Modules'].push(mod);
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
return groups;
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
/**
|
|
803
|
-
* Get sequence diagram arrow style
|
|
804
|
-
* @param {string} type - Message type
|
|
805
|
-
* @returns {string} Arrow string
|
|
806
|
-
*/
|
|
807
|
-
getSequenceArrow(type) {
|
|
808
|
-
switch (type) {
|
|
809
|
-
case 'sync': return '->>';
|
|
810
|
-
case 'async': return '->>+';
|
|
811
|
-
case 'reply': return '-->>';
|
|
812
|
-
case 'self': return '->>+';
|
|
813
|
-
case 'create': return '->>+';
|
|
814
|
-
case 'destroy': return '-x';
|
|
815
|
-
default: return '->>';
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
/**
|
|
820
|
-
* Get ER diagram cardinality notation
|
|
821
|
-
* @param {string} type - Relationship type
|
|
822
|
-
* @returns {string} Cardinality string
|
|
823
|
-
*/
|
|
824
|
-
getERCardinality(type) {
|
|
825
|
-
switch (type) {
|
|
826
|
-
case 'one-to-one': return '||--||';
|
|
827
|
-
case 'one-to-many': return '||--o{';
|
|
828
|
-
case 'many-to-one': return '}o--||';
|
|
829
|
-
case 'many-to-many': return '}o--o{';
|
|
830
|
-
case 'zero-or-one': return '|o--||';
|
|
831
|
-
case 'zero-or-many': return '}o--||';
|
|
832
|
-
default: return '||--||';
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
/**
|
|
837
|
-
* Get class diagram relationship arrow
|
|
838
|
-
* @param {string} type - Relationship type
|
|
839
|
-
* @returns {string} Arrow string
|
|
840
|
-
*/
|
|
841
|
-
getClassRelationArrow(type) {
|
|
842
|
-
switch (type) {
|
|
843
|
-
case 'extends': return '<|--';
|
|
844
|
-
case 'implements': return '<|..';
|
|
845
|
-
case 'composition': return '*--';
|
|
846
|
-
case 'aggregation': return 'o--';
|
|
847
|
-
case 'association': return '-->';
|
|
848
|
-
case 'dependency': return '..>';
|
|
849
|
-
default: return '-->';
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
/**
|
|
854
|
-
* Get visibility symbol for class members
|
|
855
|
-
* @param {string} visibility - Visibility level
|
|
856
|
-
* @returns {string} Symbol
|
|
857
|
-
*/
|
|
858
|
-
getVisibilitySymbol(visibility) {
|
|
859
|
-
switch (visibility) {
|
|
860
|
-
case 'public': return '+';
|
|
861
|
-
case 'private': return '-';
|
|
862
|
-
case 'protected': return '#';
|
|
863
|
-
case 'package': return '~';
|
|
864
|
-
default: return '+';
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
/**
|
|
870
|
-
* Create a quick architecture diagram from simple config
|
|
871
|
-
* @param {object} config - Simple architecture config
|
|
872
|
-
* @returns {string} Mermaid diagram code
|
|
873
|
-
*/
|
|
874
|
-
function quickArchitectureDiagram(config) {
|
|
875
|
-
const generator = new VisualDocGenerator();
|
|
876
|
-
return generator.generateArchitectureDiagram({
|
|
877
|
-
layers: config.layers || {},
|
|
878
|
-
connections: config.connections || []
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
/**
|
|
883
|
-
* Create a quick dependency diagram
|
|
884
|
-
* @param {Array} modules - Module list with dependencies
|
|
885
|
-
* @returns {string} Mermaid diagram code
|
|
886
|
-
*/
|
|
887
|
-
function quickDependencyDiagram(modules) {
|
|
888
|
-
const generator = new VisualDocGenerator();
|
|
889
|
-
|
|
890
|
-
const dependencies = [];
|
|
891
|
-
for (const mod of modules) {
|
|
892
|
-
for (const dep of mod.dependencies || []) {
|
|
893
|
-
dependencies.push({ from: mod.name, to: dep });
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
return generator.generateDependencyDiagram({
|
|
898
|
-
modules,
|
|
899
|
-
dependencies
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
module.exports = {
|
|
904
|
-
VisualDocGenerator,
|
|
905
|
-
quickArchitectureDiagram,
|
|
906
|
-
quickDependencyDiagram,
|
|
907
|
-
DIAGRAM_TYPES,
|
|
908
|
-
DIAGRAM_COLORS,
|
|
909
|
-
LAYER_STYLES
|
|
910
|
-
};
|