@wtdlee/repomap 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/LICENSE +21 -0
- package/README.md +527 -0
- package/dist/analyzers/base-analyzer.d.ts +46 -0
- package/dist/analyzers/base-analyzer.d.ts.map +1 -0
- package/dist/analyzers/base-analyzer.js +48 -0
- package/dist/analyzers/base-analyzer.js.map +1 -0
- package/dist/analyzers/dataflow-analyzer.d.ts +30 -0
- package/dist/analyzers/dataflow-analyzer.d.ts.map +1 -0
- package/dist/analyzers/dataflow-analyzer.js +426 -0
- package/dist/analyzers/dataflow-analyzer.js.map +1 -0
- package/dist/analyzers/graphql-analyzer.d.ts +23 -0
- package/dist/analyzers/graphql-analyzer.d.ts.map +1 -0
- package/dist/analyzers/graphql-analyzer.js +387 -0
- package/dist/analyzers/graphql-analyzer.js.map +1 -0
- package/dist/analyzers/index.d.ts +6 -0
- package/dist/analyzers/index.d.ts.map +1 -0
- package/dist/analyzers/index.js +6 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/pages-analyzer.d.ts +85 -0
- package/dist/analyzers/pages-analyzer.d.ts.map +1 -0
- package/dist/analyzers/pages-analyzer.js +1696 -0
- package/dist/analyzers/pages-analyzer.js.map +1 -0
- package/dist/analyzers/rails/index.d.ts +47 -0
- package/dist/analyzers/rails/index.d.ts.map +1 -0
- package/dist/analyzers/rails/index.js +146 -0
- package/dist/analyzers/rails/index.js.map +1 -0
- package/dist/analyzers/rails/rails-controller-analyzer.d.ts +83 -0
- package/dist/analyzers/rails/rails-controller-analyzer.d.ts.map +1 -0
- package/dist/analyzers/rails/rails-controller-analyzer.js +479 -0
- package/dist/analyzers/rails/rails-controller-analyzer.js.map +1 -0
- package/dist/analyzers/rails/rails-grpc-analyzer.d.ts +45 -0
- package/dist/analyzers/rails/rails-grpc-analyzer.d.ts.map +1 -0
- package/dist/analyzers/rails/rails-grpc-analyzer.js +263 -0
- package/dist/analyzers/rails/rails-grpc-analyzer.js.map +1 -0
- package/dist/analyzers/rails/rails-model-analyzer.d.ts +89 -0
- package/dist/analyzers/rails/rails-model-analyzer.d.ts.map +1 -0
- package/dist/analyzers/rails/rails-model-analyzer.js +494 -0
- package/dist/analyzers/rails/rails-model-analyzer.js.map +1 -0
- package/dist/analyzers/rails/rails-react-analyzer.d.ts +42 -0
- package/dist/analyzers/rails/rails-react-analyzer.d.ts.map +1 -0
- package/dist/analyzers/rails/rails-react-analyzer.js +530 -0
- package/dist/analyzers/rails/rails-react-analyzer.js.map +1 -0
- package/dist/analyzers/rails/rails-routes-analyzer.d.ts +63 -0
- package/dist/analyzers/rails/rails-routes-analyzer.d.ts.map +1 -0
- package/dist/analyzers/rails/rails-routes-analyzer.js +541 -0
- package/dist/analyzers/rails/rails-routes-analyzer.js.map +1 -0
- package/dist/analyzers/rails/rails-view-analyzer.d.ts +50 -0
- package/dist/analyzers/rails/rails-view-analyzer.d.ts.map +1 -0
- package/dist/analyzers/rails/rails-view-analyzer.js +387 -0
- package/dist/analyzers/rails/rails-view-analyzer.js.map +1 -0
- package/dist/analyzers/rails/ruby-parser.d.ts +64 -0
- package/dist/analyzers/rails/ruby-parser.d.ts.map +1 -0
- package/dist/analyzers/rails/ruby-parser.js +213 -0
- package/dist/analyzers/rails/ruby-parser.js.map +1 -0
- package/dist/analyzers/rest-api-analyzer.d.ts +66 -0
- package/dist/analyzers/rest-api-analyzer.d.ts.map +1 -0
- package/dist/analyzers/rest-api-analyzer.js +480 -0
- package/dist/analyzers/rest-api-analyzer.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +550 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/cache.d.ts +48 -0
- package/dist/core/cache.d.ts.map +1 -0
- package/dist/core/cache.js +152 -0
- package/dist/core/cache.js.map +1 -0
- package/dist/core/engine.d.ts +47 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +320 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/generators/assets/common.css +187 -0
- package/dist/generators/assets/docs.css +363 -0
- package/dist/generators/assets/page-map.css +305 -0
- package/dist/generators/assets/rails-map.css +473 -0
- package/dist/generators/index.d.ts +4 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +4 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/markdown-generator.d.ts +26 -0
- package/dist/generators/markdown-generator.d.ts.map +1 -0
- package/dist/generators/markdown-generator.js +783 -0
- package/dist/generators/markdown-generator.js.map +1 -0
- package/dist/generators/mermaid-generator.d.ts +36 -0
- package/dist/generators/mermaid-generator.d.ts.map +1 -0
- package/dist/generators/mermaid-generator.js +365 -0
- package/dist/generators/mermaid-generator.js.map +1 -0
- package/dist/generators/page-map-generator.d.ts +23 -0
- package/dist/generators/page-map-generator.d.ts.map +1 -0
- package/dist/generators/page-map-generator.js +3563 -0
- package/dist/generators/page-map-generator.js.map +1 -0
- package/dist/generators/rails-map-generator.d.ts +22 -0
- package/dist/generators/rails-map-generator.d.ts.map +1 -0
- package/dist/generators/rails-map-generator.js +909 -0
- package/dist/generators/rails-map-generator.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/server/doc-server.d.ts +31 -0
- package/dist/server/doc-server.d.ts.map +1 -0
- package/dist/server/doc-server.js +1233 -0
- package/dist/server/doc-server.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -0
- package/dist/types.d.ts +294 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/env-detector.d.ts +32 -0
- package/dist/utils/env-detector.d.ts.map +1 -0
- package/dist/utils/env-detector.js +189 -0
- package/dist/utils/env-detector.js.map +1 -0
- package/dist/utils/parallel.d.ts +24 -0
- package/dist/utils/parallel.d.ts.map +1 -0
- package/dist/utils/parallel.js +71 -0
- package/dist/utils/parallel.js.map +1 -0
- package/package.json +131 -0
|
@@ -0,0 +1,783 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown documentation generator
|
|
3
|
+
* Markdownドキュメント生成器
|
|
4
|
+
*/
|
|
5
|
+
export class MarkdownGenerator {
|
|
6
|
+
/**
|
|
7
|
+
* Generate complete documentation
|
|
8
|
+
*/
|
|
9
|
+
generateDocumentation(report) {
|
|
10
|
+
const docs = new Map();
|
|
11
|
+
// Index page
|
|
12
|
+
docs.set('index.md', this.generateIndex(report));
|
|
13
|
+
// Repository pages
|
|
14
|
+
for (const repo of report.repositories) {
|
|
15
|
+
docs.set(`repos/${repo.name}/index.md`, this.generateRepoIndex(repo));
|
|
16
|
+
docs.set(`repos/${repo.name}/pages.md`, this.generatePagesDoc(repo));
|
|
17
|
+
docs.set(`repos/${repo.name}/components.md`, this.generateComponentsDoc(repo));
|
|
18
|
+
docs.set(`repos/${repo.name}/graphql.md`, this.generateGraphQLDoc(repo));
|
|
19
|
+
docs.set(`repos/${repo.name}/dataflow.md`, this.generateDataFlowDoc(repo));
|
|
20
|
+
}
|
|
21
|
+
// Cross-repo analysis
|
|
22
|
+
docs.set('cross-repo.md', this.generateCrossRepoDoc(report));
|
|
23
|
+
// Diagrams
|
|
24
|
+
docs.set('diagrams.md', this.generateDiagramsDoc(report.diagrams));
|
|
25
|
+
return docs;
|
|
26
|
+
}
|
|
27
|
+
generateIndex(report) {
|
|
28
|
+
// Use first repository name or generic title
|
|
29
|
+
const title = report.repositories.length === 1
|
|
30
|
+
? `${report.repositories[0].displayName} Documentation`
|
|
31
|
+
: 'Project Documentation';
|
|
32
|
+
const lines = [
|
|
33
|
+
`# ${title}`,
|
|
34
|
+
'',
|
|
35
|
+
`Generated: ${report.generatedAt}`,
|
|
36
|
+
'',
|
|
37
|
+
report.repositories.length > 1 ? '## Repositories' : '## Overview',
|
|
38
|
+
'',
|
|
39
|
+
];
|
|
40
|
+
for (const repo of report.repositories) {
|
|
41
|
+
lines.push(`### [${repo.displayName}](/docs/repos/${repo.name}/index)`);
|
|
42
|
+
lines.push('');
|
|
43
|
+
lines.push(`- **Version**: ${repo.version}`);
|
|
44
|
+
lines.push(`- **Commit**: \`${repo.commitHash.substring(0, 7)}\``);
|
|
45
|
+
lines.push(`- **Pages**: ${repo.summary.totalPages}`);
|
|
46
|
+
lines.push(`- **Components**: ${repo.summary.totalComponents}`);
|
|
47
|
+
lines.push(`- **GraphQL Ops**: ${repo.summary.totalGraphQLOperations}`);
|
|
48
|
+
lines.push('');
|
|
49
|
+
}
|
|
50
|
+
lines.push('## Quick Links');
|
|
51
|
+
lines.push('');
|
|
52
|
+
lines.push('- [Cross Repository](/docs/cross-repo)');
|
|
53
|
+
lines.push('- [Diagrams](/docs/diagrams)');
|
|
54
|
+
lines.push('- [Page Map (Interactive)](/page-map)');
|
|
55
|
+
lines.push('');
|
|
56
|
+
return lines.join('\n');
|
|
57
|
+
}
|
|
58
|
+
generateRepoIndex(repo) {
|
|
59
|
+
const lines = [
|
|
60
|
+
`# ${repo.displayName}`,
|
|
61
|
+
'',
|
|
62
|
+
`Version: ${repo.version} | Commit: \`${repo.commitHash.substring(0, 7)}\``,
|
|
63
|
+
'',
|
|
64
|
+
'## Overview',
|
|
65
|
+
'',
|
|
66
|
+
`| Metric | Count |`,
|
|
67
|
+
`|--------|-------|`,
|
|
68
|
+
`| Pages | ${repo.summary.totalPages} |`,
|
|
69
|
+
`| Components | ${repo.summary.totalComponents} |`,
|
|
70
|
+
`| GraphQL Operations | ${repo.summary.totalGraphQLOperations} |`,
|
|
71
|
+
`| Data Flows | ${repo.summary.totalDataFlows} |`,
|
|
72
|
+
`| Auth Required | ${repo.summary.authRequiredPages} |`,
|
|
73
|
+
`| Public | ${repo.summary.publicPages} |`,
|
|
74
|
+
'',
|
|
75
|
+
'## Documentation',
|
|
76
|
+
'',
|
|
77
|
+
`- [Pages](/docs/repos/${repo.name}/pages)`,
|
|
78
|
+
`- [Components](/docs/repos/${repo.name}/components)`,
|
|
79
|
+
`- [GraphQL](/docs/repos/${repo.name}/graphql)`,
|
|
80
|
+
`- [Data Flow](/docs/repos/${repo.name}/dataflow)`,
|
|
81
|
+
'',
|
|
82
|
+
'## Quick Access',
|
|
83
|
+
'',
|
|
84
|
+
`- [Page Map](/page-map)`,
|
|
85
|
+
`- [Diagrams](/docs/diagrams)`,
|
|
86
|
+
'',
|
|
87
|
+
];
|
|
88
|
+
return lines.join('\n');
|
|
89
|
+
}
|
|
90
|
+
generatePagesDoc(repo) {
|
|
91
|
+
const lines = [`# ${repo.displayName} - Pages`, ''];
|
|
92
|
+
// Overview statistics
|
|
93
|
+
const authRequired = repo.analysis.pages.filter((p) => p.authentication.required).length;
|
|
94
|
+
const withQueries = repo.analysis.pages.filter((p) => p.dataFetching.some((df) => !df.type.includes('Mutation'))).length;
|
|
95
|
+
const withMutations = repo.analysis.pages.filter((p) => p.dataFetching.some((df) => df.type.includes('Mutation'))).length;
|
|
96
|
+
lines.push('| Metric | Value |');
|
|
97
|
+
lines.push('|--------|-------|');
|
|
98
|
+
lines.push(`| Total | **${repo.analysis.pages.length}** |`);
|
|
99
|
+
lines.push(`| Auth Required | ${authRequired} |`);
|
|
100
|
+
lines.push(`| With Queries | ${withQueries} |`);
|
|
101
|
+
lines.push(`| With Mutations | ${withMutations} |`);
|
|
102
|
+
lines.push('');
|
|
103
|
+
// Group by URL category
|
|
104
|
+
const byCategory = new Map();
|
|
105
|
+
for (const page of repo.analysis.pages) {
|
|
106
|
+
const category = page.path.split('/')[1] || 'root';
|
|
107
|
+
const existing = byCategory.get(category) || [];
|
|
108
|
+
existing.push(page);
|
|
109
|
+
byCategory.set(category, existing);
|
|
110
|
+
}
|
|
111
|
+
for (const [category, pages] of byCategory) {
|
|
112
|
+
lines.push(`## /${category}`);
|
|
113
|
+
lines.push('');
|
|
114
|
+
lines.push('| Page | Auth | Layout | Data |');
|
|
115
|
+
lines.push('|------|------|--------|------|');
|
|
116
|
+
for (const page of pages) {
|
|
117
|
+
const pathDisplay = page.path.replace(`/${category}`, '') || '/';
|
|
118
|
+
const auth = page.authentication.required ? 'Required' : 'Public';
|
|
119
|
+
const layout = page.layout || '-';
|
|
120
|
+
const dataOps = [];
|
|
121
|
+
const queries = page.dataFetching.filter((df) => !df.type.includes('Mutation'));
|
|
122
|
+
const mutations = page.dataFetching.filter((df) => df.type.includes('Mutation'));
|
|
123
|
+
// Show first 2 queries - use original name for consistency, filter empty
|
|
124
|
+
for (const q of queries.slice(0, 2)) {
|
|
125
|
+
const rawName = q.operationName || '';
|
|
126
|
+
if (!rawName || rawName.trim().length < 2)
|
|
127
|
+
continue;
|
|
128
|
+
const isRef = rawName.startsWith('→') || rawName.startsWith('->');
|
|
129
|
+
const cleanName = rawName.replace(/^[→\->\s]+/, '');
|
|
130
|
+
if (cleanName && cleanName.trim().length > 0) {
|
|
131
|
+
if (isRef) {
|
|
132
|
+
dataOps.push(`<span class="gql-ref" data-ref="${cleanName}" title="Component">${cleanName}</span>`);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
dataOps.push(`<span class="gql-op" data-op="${cleanName}">${cleanName}</span>`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Show first 2 mutations - use original name for consistency, filter empty
|
|
140
|
+
for (const m of mutations.slice(0, 2)) {
|
|
141
|
+
const rawName = m.operationName || '';
|
|
142
|
+
if (!rawName || rawName.trim().length < 2)
|
|
143
|
+
continue;
|
|
144
|
+
const isRef = rawName.startsWith('→') || rawName.startsWith('->');
|
|
145
|
+
const cleanName = rawName.replace(/^[→\->\s]+/, '');
|
|
146
|
+
if (cleanName && cleanName.trim().length > 0) {
|
|
147
|
+
if (isRef) {
|
|
148
|
+
dataOps.push(`<span class="gql-ref mutation" data-ref="${cleanName}" title="Component">${cleanName}</span>`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
dataOps.push(`<span class="gql-op mutation" data-op="${cleanName}">${cleanName}</span>`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// If there are more, add a "more" button
|
|
156
|
+
const remaining = queries.length +
|
|
157
|
+
mutations.length -
|
|
158
|
+
Math.min(queries.length, 2) -
|
|
159
|
+
Math.min(mutations.length, 2);
|
|
160
|
+
if (remaining > 0) {
|
|
161
|
+
dataOps.push(`<span class="gql-more" data-type="all" data-page="${page.path}">+${remaining} more</span>`);
|
|
162
|
+
}
|
|
163
|
+
const data = dataOps.length > 0 ? dataOps.join(' ') : '-';
|
|
164
|
+
lines.push(`| \`${pathDisplay}\` | ${auth} | ${layout} | ${data} |`);
|
|
165
|
+
}
|
|
166
|
+
lines.push('');
|
|
167
|
+
// Details for each page with GraphQL operations
|
|
168
|
+
for (const page of pages) {
|
|
169
|
+
const queries = page.dataFetching.filter((df) => !df.type.includes('Mutation'));
|
|
170
|
+
const mutations = page.dataFetching.filter((df) => df.type.includes('Mutation'));
|
|
171
|
+
if (queries.length > 0 || mutations.length > 0) {
|
|
172
|
+
lines.push(`### ${page.path}`);
|
|
173
|
+
lines.push('');
|
|
174
|
+
lines.push(`> ${page.filePath}`);
|
|
175
|
+
lines.push('');
|
|
176
|
+
// Queries section - filter empty
|
|
177
|
+
const validQs = queries.filter((q) => q.operationName && q.operationName.trim().length >= 2);
|
|
178
|
+
if (validQs.length > 0) {
|
|
179
|
+
lines.push(`**Queries (${validQs.length})**`);
|
|
180
|
+
lines.push('');
|
|
181
|
+
lines.push('<div class="gql-ops-list">');
|
|
182
|
+
for (const q of validQs) {
|
|
183
|
+
const rawName = q.operationName || '';
|
|
184
|
+
const isRef = rawName.startsWith('→') || rawName.startsWith('->');
|
|
185
|
+
const cleanName = rawName.replace(/^[→\->\s]+/, '');
|
|
186
|
+
if (isRef) {
|
|
187
|
+
lines.push(`<span class="gql-ref" data-ref="${cleanName}" title="Component">${cleanName}</span>`);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
lines.push(`<span class="gql-op" data-op="${cleanName}">${cleanName}</span>`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
lines.push('</div>');
|
|
194
|
+
lines.push('');
|
|
195
|
+
}
|
|
196
|
+
// Mutations section - filter empty
|
|
197
|
+
const validMuts = mutations.filter((m) => m.operationName && m.operationName.trim().length >= 2);
|
|
198
|
+
if (validMuts.length > 0) {
|
|
199
|
+
lines.push(`**Mutations (${validMuts.length})**`);
|
|
200
|
+
lines.push('');
|
|
201
|
+
lines.push('<div class="gql-ops-list">');
|
|
202
|
+
for (const m of validMuts) {
|
|
203
|
+
const rawName = m.operationName || '';
|
|
204
|
+
const isRef = rawName.startsWith('→') || rawName.startsWith('->');
|
|
205
|
+
const cleanName = rawName.replace(/^[→\->\s]+/, '');
|
|
206
|
+
if (isRef) {
|
|
207
|
+
lines.push(`<span class="gql-ref mutation" data-ref="${cleanName}" title="Component">${cleanName}</span>`);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
lines.push(`<span class="gql-op mutation" data-op="${cleanName}">${cleanName}</span>`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
lines.push('</div>');
|
|
214
|
+
lines.push('');
|
|
215
|
+
}
|
|
216
|
+
lines.push('');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return lines.join('\n');
|
|
221
|
+
}
|
|
222
|
+
generateComponentsDoc(repo) {
|
|
223
|
+
const lines = [`# ${repo.displayName} - Components`, ''];
|
|
224
|
+
// Statistics
|
|
225
|
+
const byType = new Map();
|
|
226
|
+
for (const comp of repo.analysis.components) {
|
|
227
|
+
const existing = byType.get(comp.type) || [];
|
|
228
|
+
existing.push(comp);
|
|
229
|
+
byType.set(comp.type, existing);
|
|
230
|
+
}
|
|
231
|
+
lines.push('| Type | Count |');
|
|
232
|
+
lines.push('|------|-------|');
|
|
233
|
+
lines.push(`| Container | ${byType.get('container')?.length || 0} |`);
|
|
234
|
+
lines.push(`| Presentational | ${byType.get('presentational')?.length || 0} |`);
|
|
235
|
+
lines.push(`| Layout | ${byType.get('layout')?.length || 0} |`);
|
|
236
|
+
lines.push(`| Hook | ${byType.get('hook')?.length || 0} |`);
|
|
237
|
+
lines.push(`| **Total** | **${repo.analysis.components.length}** |`);
|
|
238
|
+
lines.push('');
|
|
239
|
+
// Build page -> component mapping
|
|
240
|
+
const pageComponents = new Map();
|
|
241
|
+
for (const page of repo.analysis.pages) {
|
|
242
|
+
pageComponents.set(page.path, []);
|
|
243
|
+
}
|
|
244
|
+
for (const comp of repo.analysis.components) {
|
|
245
|
+
for (const page of repo.analysis.pages) {
|
|
246
|
+
const pageFeature = this.extractFeatureFromPage(page.filePath);
|
|
247
|
+
const compFeature = this.extractFeatureFromComponent(comp.filePath);
|
|
248
|
+
if (pageFeature && compFeature && pageFeature === compFeature) {
|
|
249
|
+
pageComponents.get(page.path)?.push(comp);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// Page-based component structure
|
|
254
|
+
lines.push('## By Page');
|
|
255
|
+
lines.push('');
|
|
256
|
+
for (const [pagePath, components] of pageComponents) {
|
|
257
|
+
if (components.length === 0)
|
|
258
|
+
continue;
|
|
259
|
+
const containers = components.filter((c) => c.type === 'container');
|
|
260
|
+
const presentationals = components.filter((c) => c.type === 'presentational');
|
|
261
|
+
const hooks = components.filter((c) => c.type === 'hook');
|
|
262
|
+
lines.push(`### ${pagePath}`);
|
|
263
|
+
lines.push('');
|
|
264
|
+
lines.push('| Component | Type | Data |');
|
|
265
|
+
lines.push('|-----------|------|------|');
|
|
266
|
+
for (const comp of containers) {
|
|
267
|
+
const dataOps = this.formatComponentDataOps(comp);
|
|
268
|
+
lines.push(`| ${comp.name} | Container | ${dataOps || '-'} |`);
|
|
269
|
+
}
|
|
270
|
+
for (const comp of presentationals.slice(0, 10)) {
|
|
271
|
+
lines.push(`| ${comp.name} | UI | - |`);
|
|
272
|
+
}
|
|
273
|
+
if (presentationals.length > 10) {
|
|
274
|
+
lines.push(`| *+${presentationals.length - 10} more* | UI | - |`);
|
|
275
|
+
}
|
|
276
|
+
for (const comp of hooks) {
|
|
277
|
+
lines.push(`| ${comp.name} | Hook | - |`);
|
|
278
|
+
}
|
|
279
|
+
lines.push('');
|
|
280
|
+
}
|
|
281
|
+
// Type summary tables
|
|
282
|
+
lines.push('## By Type');
|
|
283
|
+
lines.push('');
|
|
284
|
+
for (const [type, components] of byType) {
|
|
285
|
+
lines.push(`### ${type.charAt(0).toUpperCase() + type.slice(1)} (${components.length})`);
|
|
286
|
+
lines.push('');
|
|
287
|
+
lines.push('| Name | File |');
|
|
288
|
+
lines.push('|------|------|');
|
|
289
|
+
for (const comp of components.slice(0, 25)) {
|
|
290
|
+
const shortPath = comp.filePath.replace('src/features/', '').replace('src/', '');
|
|
291
|
+
lines.push(`| ${comp.name} | ${shortPath} |`);
|
|
292
|
+
}
|
|
293
|
+
if (components.length > 25) {
|
|
294
|
+
lines.push(`| *+${components.length - 25} more* | |`);
|
|
295
|
+
}
|
|
296
|
+
lines.push('');
|
|
297
|
+
}
|
|
298
|
+
return lines.join('\n');
|
|
299
|
+
}
|
|
300
|
+
extractFeatureFromPage(filePath) {
|
|
301
|
+
// Extract feature name from page file like "contract/billing_information.tsx" -> "contract"
|
|
302
|
+
const parts = filePath.split('/');
|
|
303
|
+
if (parts.length > 1) {
|
|
304
|
+
return parts[0];
|
|
305
|
+
}
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
extractFeatureFromComponent(filePath) {
|
|
309
|
+
// Extract feature name from component file like "src/features/contract/..." -> "contract"
|
|
310
|
+
const match = filePath.match(/src\/features\/([^/]+)/);
|
|
311
|
+
if (match) {
|
|
312
|
+
return match[1];
|
|
313
|
+
}
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
formatComponentDataOps(comp) {
|
|
317
|
+
const queries = [];
|
|
318
|
+
const mutations = [];
|
|
319
|
+
for (const hook of comp.hooks) {
|
|
320
|
+
const queryMatch = hook.match(/(?:useQuery|Query):\s*(\w+)/);
|
|
321
|
+
const mutationMatch = hook.match(/(?:useMutation|Mutation):\s*(\w+)/);
|
|
322
|
+
if (queryMatch && queryMatch[1] && queryMatch[1].trim().length >= 2) {
|
|
323
|
+
queries.push(queryMatch[1]);
|
|
324
|
+
}
|
|
325
|
+
else if (mutationMatch && mutationMatch[1] && mutationMatch[1].trim().length >= 2) {
|
|
326
|
+
mutations.push(mutationMatch[1]);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// Filter out empty or invalid names
|
|
330
|
+
const validQueries = queries.filter((q) => q && q.trim().length >= 2);
|
|
331
|
+
const validMutations = mutations.filter((m) => m && m.trim().length >= 2);
|
|
332
|
+
if (validQueries.length === 0 && validMutations.length === 0) {
|
|
333
|
+
return '';
|
|
334
|
+
}
|
|
335
|
+
const ops = [];
|
|
336
|
+
const maxShowQueries = 2;
|
|
337
|
+
const maxShowMutations = 2;
|
|
338
|
+
// Show first N queries - use original name for consistency
|
|
339
|
+
const shownQueries = validQueries.slice(0, maxShowQueries);
|
|
340
|
+
for (const name of shownQueries) {
|
|
341
|
+
ops.push(`<span class="gql-op" data-op="${name}">${name}</span>`);
|
|
342
|
+
}
|
|
343
|
+
// Show first N mutations - use original name for consistency
|
|
344
|
+
const shownMutations = validMutations.slice(0, maxShowMutations);
|
|
345
|
+
for (const name of shownMutations) {
|
|
346
|
+
ops.push(`<span class="gql-op mutation" data-op="${name}">${name}</span>`);
|
|
347
|
+
}
|
|
348
|
+
// Calculate remaining correctly
|
|
349
|
+
const hiddenQueries = validQueries.slice(maxShowQueries);
|
|
350
|
+
const hiddenMutations = validMutations.slice(maxShowMutations);
|
|
351
|
+
const remaining = hiddenQueries.length + hiddenMutations.length;
|
|
352
|
+
if (remaining > 0) {
|
|
353
|
+
// Store all hidden operations in data attributes for accurate display
|
|
354
|
+
const allQueries = JSON.stringify(validQueries).replace(/"/g, '"');
|
|
355
|
+
const allMutations = JSON.stringify(validMutations).replace(/"/g, '"');
|
|
356
|
+
ops.push(`<span class="gql-ref" data-ref="${comp.name}" data-queries="${allQueries}" data-mutations="${allMutations}" title="View all ${validQueries.length} queries and ${validMutations.length} mutations">+${remaining} more</span>`);
|
|
357
|
+
}
|
|
358
|
+
return `<div class="gql-ops-inline">${ops.join(' ')}</div>`;
|
|
359
|
+
}
|
|
360
|
+
generateGraphQLDoc(repo) {
|
|
361
|
+
const lines = [`# ${repo.displayName} - GraphQL`, ''];
|
|
362
|
+
const queries = repo.analysis.graphqlOperations.filter((op) => op.type === 'query');
|
|
363
|
+
const mutations = repo.analysis.graphqlOperations.filter((op) => op.type === 'mutation');
|
|
364
|
+
const fragments = repo.analysis.graphqlOperations.filter((op) => op.type === 'fragment');
|
|
365
|
+
lines.push('| Type | Count |');
|
|
366
|
+
lines.push('|------|-------|');
|
|
367
|
+
lines.push(`| Query | ${queries.length} |`);
|
|
368
|
+
lines.push(`| Mutation | ${mutations.length} |`);
|
|
369
|
+
lines.push(`| Fragment | ${fragments.length} |`);
|
|
370
|
+
lines.push(`| **Total** | **${repo.analysis.graphqlOperations.length}** |`);
|
|
371
|
+
lines.push('');
|
|
372
|
+
// Queries
|
|
373
|
+
if (queries.length > 0) {
|
|
374
|
+
lines.push(`## Queries`);
|
|
375
|
+
lines.push('');
|
|
376
|
+
for (const query of queries.slice(0, 80)) {
|
|
377
|
+
lines.push(`### ${query.name}`);
|
|
378
|
+
lines.push('');
|
|
379
|
+
const returnType = query.returnType || 'unknown';
|
|
380
|
+
const varsCount = query.variables.length;
|
|
381
|
+
const usedCount = query.usedIn.length;
|
|
382
|
+
lines.push(`> Return: \`${returnType}\` | Variables: ${varsCount} | Used: ${usedCount} files`);
|
|
383
|
+
lines.push('');
|
|
384
|
+
if (query.variables.length > 0) {
|
|
385
|
+
lines.push('| Variable | Type |');
|
|
386
|
+
lines.push('|----------|------|');
|
|
387
|
+
for (const v of query.variables) {
|
|
388
|
+
lines.push(`| ${v.name} | \`${v.type}\` |`);
|
|
389
|
+
}
|
|
390
|
+
lines.push('');
|
|
391
|
+
}
|
|
392
|
+
if (query.fields && query.fields.length > 0) {
|
|
393
|
+
lines.push('```graphql');
|
|
394
|
+
lines.push(this.formatGraphQLFields(query.fields, 0));
|
|
395
|
+
lines.push('```');
|
|
396
|
+
lines.push('');
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (queries.length > 80)
|
|
400
|
+
lines.push(`*+${queries.length - 80} more queries*\n`);
|
|
401
|
+
}
|
|
402
|
+
// Mutations
|
|
403
|
+
if (mutations.length > 0) {
|
|
404
|
+
lines.push(`## Mutations`);
|
|
405
|
+
lines.push('');
|
|
406
|
+
for (const mutation of mutations.slice(0, 80)) {
|
|
407
|
+
lines.push(`### ${mutation.name}`);
|
|
408
|
+
lines.push('');
|
|
409
|
+
const varsCount = mutation.variables.length;
|
|
410
|
+
const usedCount = mutation.usedIn.length;
|
|
411
|
+
lines.push(`> Variables: ${varsCount} | Used: ${usedCount} files`);
|
|
412
|
+
lines.push('');
|
|
413
|
+
if (mutation.variables.length > 0) {
|
|
414
|
+
lines.push('| Variable | Type |');
|
|
415
|
+
lines.push('|----------|------|');
|
|
416
|
+
for (const v of mutation.variables) {
|
|
417
|
+
lines.push(`| ${v.name} | \`${v.type}\` |`);
|
|
418
|
+
}
|
|
419
|
+
lines.push('');
|
|
420
|
+
}
|
|
421
|
+
if (mutation.fields && mutation.fields.length > 0) {
|
|
422
|
+
lines.push('```graphql');
|
|
423
|
+
lines.push(this.formatGraphQLFields(mutation.fields, 0));
|
|
424
|
+
lines.push('```');
|
|
425
|
+
lines.push('');
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (mutations.length > 80)
|
|
429
|
+
lines.push(`*+${mutations.length - 80} more mutations*\n`);
|
|
430
|
+
}
|
|
431
|
+
// Fragments
|
|
432
|
+
if (fragments.length > 0) {
|
|
433
|
+
lines.push(`## Fragments`);
|
|
434
|
+
lines.push('');
|
|
435
|
+
lines.push('| Name | Type | Fields |');
|
|
436
|
+
lines.push('|------|------|--------|');
|
|
437
|
+
for (const fragment of fragments.slice(0, 50)) {
|
|
438
|
+
const fieldCount = fragment.fields?.length || 0;
|
|
439
|
+
lines.push(`| ${fragment.name} | ${fragment.returnType || '-'} | ${fieldCount} |`);
|
|
440
|
+
}
|
|
441
|
+
if (fragments.length > 50)
|
|
442
|
+
lines.push(`| *+${fragments.length - 50} more* | | |`);
|
|
443
|
+
lines.push('');
|
|
444
|
+
}
|
|
445
|
+
return lines.join('\n');
|
|
446
|
+
}
|
|
447
|
+
formatGraphQLFields(fields, indent) {
|
|
448
|
+
if (!fields || fields.length === 0)
|
|
449
|
+
return '';
|
|
450
|
+
const lines = [];
|
|
451
|
+
for (const field of fields) {
|
|
452
|
+
const prefix = ' '.repeat(indent);
|
|
453
|
+
if (field.fields && field.fields.length > 0) {
|
|
454
|
+
lines.push(`${prefix}${field.name} {`);
|
|
455
|
+
lines.push(this.formatGraphQLFields(field.fields, indent + 1));
|
|
456
|
+
lines.push(`${prefix}}`);
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
lines.push(`${prefix}${field.name}`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return lines.join('\n');
|
|
463
|
+
}
|
|
464
|
+
generateDataFlowDoc(repo) {
|
|
465
|
+
const lines = [`# ${repo.displayName} - Data Flow`, ''];
|
|
466
|
+
// Overview
|
|
467
|
+
const queryFlows = repo.analysis.dataFlows.filter((f) => f.name.includes('📡') || f.operations.some((o) => o.includes('Query')));
|
|
468
|
+
const mutationFlows = repo.analysis.dataFlows.filter((f) => f.name.includes('✏️') || f.operations.some((o) => o.includes('Mutation')));
|
|
469
|
+
const contextFlows = repo.analysis.dataFlows.filter((f) => f.source.type === 'context');
|
|
470
|
+
lines.push('## Overview');
|
|
471
|
+
lines.push('');
|
|
472
|
+
lines.push('| Type | Count | Direction |');
|
|
473
|
+
lines.push('|------|-------|-----------|');
|
|
474
|
+
lines.push(`| \`QUERY\` | ${queryFlows.length} | Server → Component |`);
|
|
475
|
+
lines.push(`| \`MUTATION\` | ${mutationFlows.length} | Component → Server |`);
|
|
476
|
+
lines.push(`| \`CONTEXT\` | ${contextFlows.length} | Provider → Consumer |`);
|
|
477
|
+
lines.push(`| **Total** | **${repo.analysis.dataFlows.length}** | |`);
|
|
478
|
+
lines.push('');
|
|
479
|
+
// Architecture diagram
|
|
480
|
+
lines.push('## Architecture');
|
|
481
|
+
lines.push('');
|
|
482
|
+
lines.push('```mermaid');
|
|
483
|
+
lines.push('flowchart LR');
|
|
484
|
+
lines.push(' subgraph Server["GraphQL Server"]');
|
|
485
|
+
lines.push(' API[(API)]');
|
|
486
|
+
lines.push(' end');
|
|
487
|
+
lines.push(' subgraph Client["React Application"]');
|
|
488
|
+
lines.push(' Apollo[Apollo Client]');
|
|
489
|
+
lines.push(' Container[Container Component]');
|
|
490
|
+
lines.push(' View[View Component]');
|
|
491
|
+
lines.push(' end');
|
|
492
|
+
lines.push(' API -->|Query Response| Apollo');
|
|
493
|
+
lines.push(' Apollo -->|Cache/Data| Container');
|
|
494
|
+
lines.push(' Container -->|Props| View');
|
|
495
|
+
lines.push(' View -->|User Action| Container');
|
|
496
|
+
lines.push(' Container -->|Mutation| Apollo');
|
|
497
|
+
lines.push(' Apollo -->|GraphQL Request| API');
|
|
498
|
+
lines.push('```');
|
|
499
|
+
lines.push('');
|
|
500
|
+
// Page-based data flow
|
|
501
|
+
lines.push('## Page Data Flows');
|
|
502
|
+
lines.push('');
|
|
503
|
+
for (const page of repo.analysis.pages) {
|
|
504
|
+
const pageFeature = this.extractFeatureFromPage(page.filePath);
|
|
505
|
+
const relatedComponents = repo.analysis.components.filter((c) => {
|
|
506
|
+
const compFeature = this.extractFeatureFromComponent(c.filePath);
|
|
507
|
+
return compFeature === pageFeature;
|
|
508
|
+
});
|
|
509
|
+
const hasDataOps = page.dataFetching.length > 0 ||
|
|
510
|
+
relatedComponents.some((c) => c.stateManagement.some((s) => s.includes('Apollo') || s.includes('Context')));
|
|
511
|
+
if (!hasDataOps)
|
|
512
|
+
continue;
|
|
513
|
+
lines.push(`### ${page.path}`);
|
|
514
|
+
lines.push('');
|
|
515
|
+
lines.push(`\`FILE: ${page.filePath}\``);
|
|
516
|
+
lines.push('');
|
|
517
|
+
// Mermaid flow diagram for this page
|
|
518
|
+
const pageOps = this.getPageOperations(page, relatedComponents, repo.analysis.graphqlOperations);
|
|
519
|
+
// Filter out empty operation names before creating diagram
|
|
520
|
+
const validQueries = pageOps.queries.filter((q) => q && q.trim().length > 0);
|
|
521
|
+
const validMutations = pageOps.mutations.filter((m) => m && m.trim().length > 0);
|
|
522
|
+
if (validQueries.length > 0 || validMutations.length > 0) {
|
|
523
|
+
lines.push('```mermaid');
|
|
524
|
+
lines.push('flowchart LR');
|
|
525
|
+
const pageId = page.path.replace(/[^a-zA-Z0-9]/g, '_');
|
|
526
|
+
// Escape special characters in path for Mermaid
|
|
527
|
+
const safePath = page.path.replace(/"/g, "'");
|
|
528
|
+
lines.push(` Page${pageId}["${safePath}"]`);
|
|
529
|
+
validQueries.slice(0, 5).forEach((q, i) => {
|
|
530
|
+
const qId = `Q${pageId}_${i}`;
|
|
531
|
+
// Escape special characters in operation name
|
|
532
|
+
const safeQ = q.replace(/"/g, "'").replace(/[<>]/g, '');
|
|
533
|
+
lines.push(` ${qId}["${safeQ}"]:::query --> Page${pageId}`);
|
|
534
|
+
});
|
|
535
|
+
validMutations.slice(0, 5).forEach((m, i) => {
|
|
536
|
+
const mId = `M${pageId}_${i}`;
|
|
537
|
+
// Escape special characters in operation name
|
|
538
|
+
const safeM = m.replace(/"/g, "'").replace(/[<>]/g, '');
|
|
539
|
+
lines.push(` Page${pageId} --> ${mId}["${safeM}"]:::mutation`);
|
|
540
|
+
});
|
|
541
|
+
lines.push(' classDef query fill:#dbeafe,stroke:#1d4ed8,color:#1e40af');
|
|
542
|
+
lines.push(' classDef mutation fill:#fce7f3,stroke:#be185d,color:#9d174d');
|
|
543
|
+
lines.push('```');
|
|
544
|
+
lines.push('');
|
|
545
|
+
}
|
|
546
|
+
// Operations list with clickable tags - grouped by type
|
|
547
|
+
if (pageOps.queries.length > 0 || pageOps.mutations.length > 0) {
|
|
548
|
+
// Queries section - use original name for consistency, filter empty
|
|
549
|
+
if (validQueries.length > 0) {
|
|
550
|
+
lines.push(`**Queries (${validQueries.length})**`);
|
|
551
|
+
lines.push('');
|
|
552
|
+
lines.push('<div class="gql-ops-list">');
|
|
553
|
+
for (const q of validQueries) {
|
|
554
|
+
const isRef = q.startsWith('→') || q.startsWith('->');
|
|
555
|
+
const cleanName = q.replace(/^[→\->\s]+/, '');
|
|
556
|
+
if (cleanName && cleanName.trim().length > 0) {
|
|
557
|
+
if (isRef) {
|
|
558
|
+
lines.push(`<span class="gql-ref" data-ref="${cleanName}" title="Component: ${cleanName}">${cleanName}</span>`);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
lines.push(`<span class="gql-op" data-op="${cleanName}">${cleanName}</span>`);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
lines.push('</div>');
|
|
566
|
+
lines.push('');
|
|
567
|
+
}
|
|
568
|
+
// Mutations section - use original name for consistency, filter empty
|
|
569
|
+
if (validMutations.length > 0) {
|
|
570
|
+
lines.push(`**Mutations (${validMutations.length})**`);
|
|
571
|
+
lines.push('');
|
|
572
|
+
lines.push('<div class="gql-ops-list">');
|
|
573
|
+
for (const m of validMutations) {
|
|
574
|
+
const isRef = m.startsWith('→') || m.startsWith('->');
|
|
575
|
+
const cleanName = m.replace(/^[→\->\s]+/, '');
|
|
576
|
+
if (cleanName && cleanName.trim().length > 0) {
|
|
577
|
+
if (isRef) {
|
|
578
|
+
lines.push(`<span class="gql-ref mutation" data-ref="${cleanName}" title="Component: ${cleanName}">${cleanName}</span>`);
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
lines.push(`<span class="gql-op mutation" data-op="${cleanName}">${cleanName}</span>`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
lines.push('</div>');
|
|
586
|
+
lines.push('');
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
lines.push('---');
|
|
590
|
+
lines.push('');
|
|
591
|
+
}
|
|
592
|
+
// Context providers
|
|
593
|
+
const providers = new Set();
|
|
594
|
+
for (const flow of contextFlows) {
|
|
595
|
+
providers.add(flow.source.name);
|
|
596
|
+
}
|
|
597
|
+
if (providers.size > 0) {
|
|
598
|
+
lines.push('## Context Providers');
|
|
599
|
+
lines.push('');
|
|
600
|
+
lines.push('| Provider | Description |');
|
|
601
|
+
lines.push('|----------|-------------|');
|
|
602
|
+
for (const provider of providers) {
|
|
603
|
+
lines.push(`| \`${provider}\` | Provides shared state |`);
|
|
604
|
+
}
|
|
605
|
+
lines.push('');
|
|
606
|
+
}
|
|
607
|
+
return lines.join('\n');
|
|
608
|
+
}
|
|
609
|
+
getPageOperations(page, relatedComponents, _allOperations) {
|
|
610
|
+
const queries = new Set();
|
|
611
|
+
const mutations = new Set();
|
|
612
|
+
// Helper to validate operation name
|
|
613
|
+
const isValidOpName = (name) => {
|
|
614
|
+
if (!name)
|
|
615
|
+
return false;
|
|
616
|
+
const trimmed = name.trim();
|
|
617
|
+
// Must have at least 2 alphanumeric characters
|
|
618
|
+
return trimmed.length >= 2 && /[a-zA-Z]/.test(trimmed);
|
|
619
|
+
};
|
|
620
|
+
// From page dataFetching
|
|
621
|
+
for (const df of page.dataFetching) {
|
|
622
|
+
const rawName = df.operationName?.replace(/^[→\->\s]+/, '') || '';
|
|
623
|
+
const name = rawName.replace(/Document$/g, '');
|
|
624
|
+
if (isValidOpName(name)) {
|
|
625
|
+
if (df.type?.includes('Mutation')) {
|
|
626
|
+
mutations.add(name);
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
queries.add(name);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
// From related components
|
|
634
|
+
for (const comp of relatedComponents) {
|
|
635
|
+
for (const hook of comp.hooks) {
|
|
636
|
+
if (hook.includes('Query')) {
|
|
637
|
+
const match = hook.match(/:\s*(.+)$/);
|
|
638
|
+
if (match && isValidOpName(match[1])) {
|
|
639
|
+
queries.add(match[1].trim());
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
if (hook.includes('Mutation')) {
|
|
643
|
+
const match = hook.match(/:\s*(.+)$/);
|
|
644
|
+
if (match && isValidOpName(match[1])) {
|
|
645
|
+
mutations.add(match[1].trim());
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
// Filter out any remaining invalid names
|
|
651
|
+
return {
|
|
652
|
+
queries: Array.from(queries).filter(isValidOpName),
|
|
653
|
+
mutations: Array.from(mutations).filter(isValidOpName),
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
generateCrossRepoDoc(report) {
|
|
657
|
+
const lines = [
|
|
658
|
+
'# クロスリポジトリ分析',
|
|
659
|
+
'',
|
|
660
|
+
'## アーキテクチャ概要',
|
|
661
|
+
'',
|
|
662
|
+
'```mermaid',
|
|
663
|
+
'flowchart TB',
|
|
664
|
+
];
|
|
665
|
+
for (const repo of report.repositories) {
|
|
666
|
+
const repoId = repo.name.replace(/[^a-zA-Z0-9]/g, '_');
|
|
667
|
+
lines.push(` subgraph ${repoId}["${repo.displayName}"]`);
|
|
668
|
+
lines.push(` ${repoId}_core["Core"]`);
|
|
669
|
+
lines.push(' end');
|
|
670
|
+
}
|
|
671
|
+
lines.push('```');
|
|
672
|
+
lines.push('');
|
|
673
|
+
// API Connections
|
|
674
|
+
lines.push('## API接続');
|
|
675
|
+
lines.push('');
|
|
676
|
+
for (const conn of report.crossRepoAnalysis.apiConnections) {
|
|
677
|
+
lines.push(`- **${conn.frontend}** → **${conn.backend}**: \`${conn.endpoint}\``);
|
|
678
|
+
}
|
|
679
|
+
lines.push('');
|
|
680
|
+
// Shared types
|
|
681
|
+
lines.push('## 共有型');
|
|
682
|
+
lines.push('');
|
|
683
|
+
for (const type of report.crossRepoAnalysis.sharedTypes) {
|
|
684
|
+
lines.push(`- \`${type}\``);
|
|
685
|
+
}
|
|
686
|
+
lines.push('');
|
|
687
|
+
return lines.join('\n');
|
|
688
|
+
}
|
|
689
|
+
createPageDataFlowDiagram(page, relatedComponents, _dataFlows) {
|
|
690
|
+
const lines = [];
|
|
691
|
+
// Extract queries and mutations from related components
|
|
692
|
+
const queries = [];
|
|
693
|
+
const mutations = [];
|
|
694
|
+
const containers = [];
|
|
695
|
+
for (const comp of relatedComponents) {
|
|
696
|
+
if (comp.type === 'container') {
|
|
697
|
+
containers.push(comp.name);
|
|
698
|
+
}
|
|
699
|
+
for (const hook of comp.hooks) {
|
|
700
|
+
if (hook.includes('Query') || hook.includes('📡')) {
|
|
701
|
+
const name = hook.includes(':') ? hook.split(':')[1].trim() : hook;
|
|
702
|
+
if (!queries.includes(name))
|
|
703
|
+
queries.push(name);
|
|
704
|
+
}
|
|
705
|
+
if (hook.includes('Mutation') || hook.includes('✏️')) {
|
|
706
|
+
const name = hook.includes(':') ? hook.split(':')[1].trim() : hook;
|
|
707
|
+
if (!mutations.includes(name))
|
|
708
|
+
mutations.push(name);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
// Also check page's own data fetching
|
|
713
|
+
for (const df of page.dataFetching) {
|
|
714
|
+
const name = df.operationName.replace(/^→\s*/, '');
|
|
715
|
+
if (df.type.includes('Query') && !queries.includes(name)) {
|
|
716
|
+
queries.push(name);
|
|
717
|
+
}
|
|
718
|
+
else if (df.type.includes('Mutation') && !mutations.includes(name)) {
|
|
719
|
+
mutations.push(name);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
// Build ASCII diagram
|
|
723
|
+
lines.push(`[Page: ${page.path}]`);
|
|
724
|
+
lines.push('│');
|
|
725
|
+
if (queries.length > 0 || mutations.length > 0) {
|
|
726
|
+
lines.push('├─ 📡 データ取得 (Query)');
|
|
727
|
+
for (const q of queries.slice(0, 5)) {
|
|
728
|
+
lines.push(`│ ├─ ${q.substring(0, 40)}`);
|
|
729
|
+
lines.push(`│ │ └─ GraphQL Server → Apollo Cache → Component`);
|
|
730
|
+
}
|
|
731
|
+
if (queries.length > 5) {
|
|
732
|
+
lines.push(`│ └─ ... 他 ${queries.length - 5} 件`);
|
|
733
|
+
}
|
|
734
|
+
if (mutations.length > 0) {
|
|
735
|
+
lines.push('│');
|
|
736
|
+
lines.push('├─ ✏️ データ更新 (Mutation)');
|
|
737
|
+
for (const m of mutations.slice(0, 5)) {
|
|
738
|
+
lines.push(`│ ├─ ${m.substring(0, 40)}`);
|
|
739
|
+
lines.push(`│ │ └─ Component → GraphQL Server → Apollo Cache`);
|
|
740
|
+
}
|
|
741
|
+
if (mutations.length > 5) {
|
|
742
|
+
lines.push(`│ └─ ... 他 ${mutations.length - 5} 件`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
if (containers.length > 0) {
|
|
747
|
+
lines.push('│');
|
|
748
|
+
lines.push('├─ 📦 Container Components');
|
|
749
|
+
for (const c of containers.slice(0, 5)) {
|
|
750
|
+
lines.push(`│ └─ ${c}`);
|
|
751
|
+
}
|
|
752
|
+
if (containers.length > 5) {
|
|
753
|
+
lines.push(`│ └─ ... 他 ${containers.length - 5} 件`);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
lines.push('│');
|
|
757
|
+
lines.push('└─ [Render]');
|
|
758
|
+
return lines.join('\n');
|
|
759
|
+
}
|
|
760
|
+
generateDiagramsDoc(diagrams) {
|
|
761
|
+
const lines = ['# Diagrams', ''];
|
|
762
|
+
lines.push('## Overview');
|
|
763
|
+
lines.push('');
|
|
764
|
+
lines.push('| Diagram | Type | Description |');
|
|
765
|
+
lines.push('|---------|------|-------------|');
|
|
766
|
+
for (const diagram of diagrams) {
|
|
767
|
+
lines.push(`| ${diagram.title} | \`${diagram.type.toUpperCase()}\` | Auto-generated |`);
|
|
768
|
+
}
|
|
769
|
+
lines.push('');
|
|
770
|
+
for (const diagram of diagrams) {
|
|
771
|
+
lines.push(`## ${diagram.title}`);
|
|
772
|
+
lines.push('');
|
|
773
|
+
lines.push(`\`TYPE: ${diagram.type.toUpperCase()}\``);
|
|
774
|
+
lines.push('');
|
|
775
|
+
lines.push('```mermaid');
|
|
776
|
+
lines.push(diagram.content);
|
|
777
|
+
lines.push('```');
|
|
778
|
+
lines.push('');
|
|
779
|
+
}
|
|
780
|
+
return lines.join('\n');
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
//# sourceMappingURL=markdown-generator.js.map
|