docstodev 1.0.0 → 1.0.3
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/package.json +2 -2
- package/src/ai/analyzer.ts +6 -2
- package/src/analyzers/languageAnalyzer.ts +580 -0
- package/src/cache/cacheManager.ts +179 -0
- package/src/cli/index.ts +307 -23
- package/src/commands/generateSummary.ts +129 -39
- package/src/commands/run.ts +312 -75
- package/src/exporters/html.ts +604 -106
- package/.env +0 -1
- package/dist/ai/analyzer.d.ts +0 -3
- package/dist/ai/analyzer.d.ts.map +0 -1
- package/dist/ai/analyzer.js +0 -42
- package/dist/ai/analyzer.js.map +0 -1
- package/dist/cli/index.d.ts +0 -3
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -59
- package/dist/cli/index.js.map +0 -1
- package/dist/commands/classify.d.ts +0 -2
- package/dist/commands/classify.d.ts.map +0 -1
- package/dist/commands/classify.js +0 -37
- package/dist/commands/classify.js.map +0 -1
- package/dist/commands/generateSummary.d.ts +0 -5
- package/dist/commands/generateSummary.d.ts.map +0 -1
- package/dist/commands/generateSummary.js +0 -54
- package/dist/commands/generateSummary.js.map +0 -1
- package/dist/commands/run.d.ts +0 -2
- package/dist/commands/run.d.ts.map +0 -1
- package/dist/commands/run.js +0 -142
- package/dist/commands/run.js.map +0 -1
- package/dist/exporters/html.d.ts +0 -2
- package/dist/exporters/html.d.ts.map +0 -1
- package/dist/exporters/html.js +0 -148
- package/dist/exporters/html.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
- package/docs/docs-to-dev.md +0 -111
- package/docs/report.html +0 -90
- package/docs/report.pdf +0 -0
- package/docs/summary.md +0 -16
- package/src/commands/classify.ts +0 -40
- package/src/index.ts +0 -0
package/src/exporters/html.ts
CHANGED
|
@@ -1,142 +1,640 @@
|
|
|
1
|
-
// Emplacement absolu : M:\workspace\extensions\docstodev\src\exporters\html.ts
|
|
2
1
|
import { writeFileSync } from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
interface TreeStructure {
|
|
5
|
+
[key: string]: TreeStructure | null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function exportToHTML(
|
|
9
|
+
docsDir: string,
|
|
10
|
+
markdownContent: string,
|
|
11
|
+
mermaidGraph: string,
|
|
12
|
+
lang: "fr" | "en" = "fr",
|
|
13
|
+
fileTree?: TreeStructure
|
|
14
|
+
) {
|
|
15
|
+
const lines = markdownContent.split("\n");
|
|
16
|
+
|
|
7
17
|
let htmlResult = "";
|
|
8
18
|
let inTable = false;
|
|
19
|
+
let inSection = false;
|
|
9
20
|
|
|
10
|
-
const formatText = (text: string) =>
|
|
11
|
-
|
|
12
|
-
.replace(/`(.*?)`/g,
|
|
13
|
-
.replace(/\*\*(.*?)\*\*/g,
|
|
21
|
+
const formatText = (text: string) =>
|
|
22
|
+
text
|
|
23
|
+
.replace(/`(.*?)`/g, "<code>$1</code>")
|
|
24
|
+
.replace(/\*\*(.*?)\*\*/g, "$1")
|
|
14
25
|
.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2">$1</a>');
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (processed.startsWith(
|
|
21
|
-
if (!inTable) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
|
|
27
|
+
for (const line of lines) {
|
|
28
|
+
const processed = line.trim();
|
|
29
|
+
|
|
30
|
+
/* ================= TABLES ================= */
|
|
31
|
+
if (processed.startsWith("|")) {
|
|
32
|
+
if (!inTable) {
|
|
33
|
+
htmlResult += `<div class="table-container"><table>`;
|
|
34
|
+
inTable = true;
|
|
35
|
+
}
|
|
36
|
+
if (processed.includes("---")) continue;
|
|
37
|
+
|
|
38
|
+
const cells = processed.split("|").filter(c => c.trim());
|
|
39
|
+
const isHeader = processed.includes("Module") || processed.includes("Type");
|
|
40
|
+
|
|
41
|
+
htmlResult += `<tr>${
|
|
42
|
+
cells.map(c =>
|
|
43
|
+
isHeader
|
|
44
|
+
? `<th>${formatText(c.trim())}</th>`
|
|
45
|
+
: `<td>${formatText(c.trim())}</td>`
|
|
46
|
+
).join("")
|
|
47
|
+
}</tr>`;
|
|
48
|
+
continue;
|
|
49
|
+
} else if (inTable) {
|
|
50
|
+
htmlResult += "</table></div>";
|
|
51
|
+
inTable = false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* ================= TREE VIEW ================= */
|
|
55
|
+
if (
|
|
56
|
+
processed.match(/^[│├└─\s]+/) ||
|
|
57
|
+
processed.includes("/") ||
|
|
58
|
+
processed.match(/\.(ts|js|py|java|cs|go|rs|tsx|jsx|html|css|php|rb|sql)$/)
|
|
59
|
+
) {
|
|
60
|
+
const hasFile = processed.match(/\.(ts|js|py|java|cs|go|rs|tsx|jsx|html|css|php|rb|sql)$/);
|
|
61
|
+
const fileMatch = hasFile ? processed.match(/([\w-]+\.(ts|js|py|java|cs|go|rs|tsx|jsx|html|css|php|rb|sql))/)?.[0] : null;
|
|
62
|
+
|
|
63
|
+
let treeLine = line
|
|
31
64
|
.replace(/├─/g, '<span class="branch">├─</span>')
|
|
32
65
|
.replace(/└─/g, '<span class="branch">└─</span>')
|
|
33
66
|
.replace(/│/g, '<span class="pipe">│</span>');
|
|
67
|
+
|
|
68
|
+
if (fileMatch) {
|
|
69
|
+
treeLine = treeLine.replace(
|
|
70
|
+
fileMatch,
|
|
71
|
+
`<span class="file-badge">${fileMatch}</span>`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
34
75
|
htmlResult += `<div class="tree-line">${formatText(treeLine)}</div>`;
|
|
35
|
-
|
|
76
|
+
continue;
|
|
36
77
|
}
|
|
37
78
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
htmlResult += `<
|
|
47
|
-
} else if (processed.
|
|
79
|
+
/* ================= HEADERS ================= */
|
|
80
|
+
if (processed.startsWith("# ")) {
|
|
81
|
+
htmlResult += `<h1>${processed.slice(2)}</h1>`;
|
|
82
|
+
} else if (processed.startsWith("## ")) {
|
|
83
|
+
if (inSection) {
|
|
84
|
+
htmlResult += "</section>";
|
|
85
|
+
inSection = false;
|
|
86
|
+
}
|
|
87
|
+
htmlResult += `<h2>${processed.slice(3)}</h2>`;
|
|
88
|
+
} else if (processed.startsWith("### ")) {
|
|
89
|
+
if (inSection) htmlResult += "</section>";
|
|
90
|
+
htmlResult += `<section class="component"><h3>${formatText(processed.slice(4))}</h3>`;
|
|
91
|
+
inSection = true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* ================= LISTS ================= */
|
|
95
|
+
else if (/^[-*•]\s+/.test(processed)) {
|
|
96
|
+
htmlResult += `<div class="list-item">${formatText(processed.replace(/^[-*•]\s+/, ""))}</div>`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* ================= PARAGRAPHS ================= */
|
|
100
|
+
else if (processed.length > 0) {
|
|
48
101
|
htmlResult += `<p>${formatText(processed)}</p>`;
|
|
49
102
|
}
|
|
50
|
-
}
|
|
103
|
+
}
|
|
51
104
|
|
|
52
|
-
if (
|
|
105
|
+
if (inSection) htmlResult += "</section>";
|
|
106
|
+
|
|
107
|
+
// Nettoyer et valider les graphes Mermaid
|
|
108
|
+
const cleanMermaidGraph = sanitizeMermaidGraph(mermaidGraph || "graph TD\n Root[Projet]");
|
|
109
|
+
const hierarchyGraph = fileTree ? generateHierarchyGraph(fileTree) : getDefaultHierarchyGraph();
|
|
110
|
+
const dataFlowGraph = generateDataFlowGraph();
|
|
53
111
|
|
|
54
112
|
const html = `<!DOCTYPE html>
|
|
55
113
|
<html lang="${lang}" data-theme="dark">
|
|
56
114
|
<head>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
|
61
|
-
<style>
|
|
62
|
-
:root[data-theme="dark"] {
|
|
63
|
-
--bg: #0d1117; --card: #161b22; --text: #c9d1d9; --text-dim: #8b949e;
|
|
64
|
-
--accent: #58a6ff; --border: #30363d; --code-bg: rgba(110, 118, 129, 0.2);
|
|
65
|
-
--code-text: #ff7b72; --h-color: #f0f6fc;
|
|
66
|
-
}
|
|
67
|
-
:root[data-theme="light"] {
|
|
68
|
-
--bg: #ffffff; --card: #f8f9fa; --text: #24292f; --text-dim: #57606a;
|
|
69
|
-
--accent: #0969da; --border: #d0d7de; --code-bg: #afb8c133;
|
|
70
|
-
--code-text: #cf222e; --h-color: #1b1f24;
|
|
71
|
-
}
|
|
115
|
+
<meta charset="UTF-8">
|
|
116
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
117
|
+
<title>DocsToDev – ${lang === 'fr' ? 'Rapport Technique' : 'Technical Report'}</title>
|
|
72
118
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
119
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400&family=Fira+Code&display=swap" rel="stylesheet">
|
|
120
|
+
<script type="module">
|
|
121
|
+
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
|
122
|
+
|
|
123
|
+
mermaid.initialize({
|
|
124
|
+
startOnLoad: true,
|
|
125
|
+
theme: document.documentElement.dataset.theme === "dark" ? "dark" : "default",
|
|
126
|
+
securityLevel: 'loose',
|
|
127
|
+
logLevel: 'error',
|
|
128
|
+
themeVariables: {
|
|
129
|
+
darkMode: document.documentElement.dataset.theme === "dark",
|
|
130
|
+
background: "transparent",
|
|
131
|
+
primaryColor: "#58a6ff",
|
|
132
|
+
primaryTextColor: "#c9d1d9",
|
|
133
|
+
primaryBorderColor: "#30363d",
|
|
134
|
+
lineColor: "#8b949e",
|
|
135
|
+
secondaryColor: "#161b22",
|
|
136
|
+
tertiaryColor: "#0d1117",
|
|
137
|
+
fontSize: "14px",
|
|
138
|
+
fontFamily: "Inter, sans-serif"
|
|
139
|
+
},
|
|
140
|
+
flowchart: {
|
|
141
|
+
useMaxWidth: true,
|
|
142
|
+
htmlLabels: true,
|
|
143
|
+
curve: 'basis'
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
window.mermaid = mermaid;
|
|
148
|
+
</script>
|
|
80
149
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
150
|
+
<style>
|
|
151
|
+
:root[data-theme="dark"] {
|
|
152
|
+
--bg: #0d1117;
|
|
153
|
+
--card: #161b22;
|
|
154
|
+
--text: #c9d1d9;
|
|
155
|
+
--muted: #8b949e;
|
|
156
|
+
--border: #30363d;
|
|
157
|
+
--accent: #58a6ff;
|
|
158
|
+
--code-bg: #1f2428;
|
|
159
|
+
--file-badge-bg: rgba(248, 81, 73, 0.15);
|
|
160
|
+
--file-badge-text: #f85149;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
:root[data-theme="light"] {
|
|
164
|
+
--bg: #ffffff;
|
|
165
|
+
--card: #f6f8fa;
|
|
166
|
+
--text: #24292f;
|
|
167
|
+
--muted: #57606a;
|
|
168
|
+
--border: #d0d7de;
|
|
169
|
+
--accent: #0969da;
|
|
170
|
+
--code-bg: #eaeef2;
|
|
171
|
+
--file-badge-bg: rgba(218, 54, 51, 0.15);
|
|
172
|
+
--file-badge-text: #da3633;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
* {
|
|
176
|
+
margin: 0;
|
|
177
|
+
padding: 0;
|
|
178
|
+
box-sizing: border-box;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
body {
|
|
182
|
+
font-family: Inter, sans-serif;
|
|
183
|
+
background: var(--bg);
|
|
184
|
+
color: var(--text);
|
|
185
|
+
line-height: 1.6;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.container {
|
|
189
|
+
max-width: 1400px;
|
|
190
|
+
margin: 0 auto;
|
|
191
|
+
padding: 2rem;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.controls {
|
|
195
|
+
position: sticky;
|
|
196
|
+
top: 0;
|
|
197
|
+
background: var(--bg);
|
|
198
|
+
padding: 1rem 0;
|
|
199
|
+
display: flex;
|
|
200
|
+
gap: 0.75rem;
|
|
201
|
+
z-index: 100;
|
|
202
|
+
border-bottom: 1px solid var(--border);
|
|
203
|
+
margin-bottom: 2rem;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
#search {
|
|
207
|
+
flex: 1;
|
|
208
|
+
padding: 0.6rem 1rem;
|
|
209
|
+
border: 1px solid var(--border);
|
|
210
|
+
background: var(--card);
|
|
211
|
+
color: var(--text);
|
|
212
|
+
font-size: 0.95rem;
|
|
213
|
+
font-family: Inter, sans-serif;
|
|
214
|
+
transition: border-color 0.2s;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
#search:focus {
|
|
218
|
+
outline: none;
|
|
219
|
+
border-color: var(--accent);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
button {
|
|
223
|
+
padding: 0.6rem 1.2rem;
|
|
224
|
+
border: 1px solid var(--border);
|
|
225
|
+
background: var(--card);
|
|
226
|
+
color: var(--text);
|
|
227
|
+
cursor: pointer;
|
|
228
|
+
font-size: 0.95rem;
|
|
229
|
+
font-family: Inter, sans-serif;
|
|
230
|
+
transition: all 0.2s;
|
|
231
|
+
display: flex;
|
|
232
|
+
align-items: center;
|
|
233
|
+
gap: 0.5rem;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
button:hover {
|
|
237
|
+
background: var(--border);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
button svg {
|
|
241
|
+
width: 16px;
|
|
242
|
+
height: 16px;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
h1 {
|
|
246
|
+
font-size: 2.5rem;
|
|
247
|
+
color: var(--accent);
|
|
248
|
+
margin-bottom: 3rem;
|
|
249
|
+
font-weight: 400;
|
|
250
|
+
letter-spacing: -0.02em;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
h2 {
|
|
254
|
+
font-size: 1.5rem;
|
|
255
|
+
margin: 3rem 0 1.5rem 0;
|
|
256
|
+
padding-bottom: 0.75rem;
|
|
257
|
+
border-bottom: 1px solid var(--border);
|
|
258
|
+
font-weight: 400;
|
|
259
|
+
color: var(--text);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
h3 {
|
|
263
|
+
font-size: 1.1rem;
|
|
264
|
+
margin-bottom: 1rem;
|
|
265
|
+
color: var(--accent);
|
|
266
|
+
font-weight: 400;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.graphs-grid {
|
|
270
|
+
display: grid;
|
|
271
|
+
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
|
|
272
|
+
gap: 2rem;
|
|
273
|
+
margin: 2rem 0;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.graph-container {
|
|
277
|
+
border: 1px solid var(--border);
|
|
278
|
+
background: var(--card);
|
|
279
|
+
padding: 1.5rem;
|
|
280
|
+
min-height: 300px;
|
|
281
|
+
position: relative;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.graph-title {
|
|
285
|
+
font-size: 1rem;
|
|
286
|
+
color: var(--muted);
|
|
287
|
+
margin-bottom: 1rem;
|
|
288
|
+
text-transform: uppercase;
|
|
289
|
+
letter-spacing: 0.05em;
|
|
290
|
+
font-size: 0.85rem;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.mermaid-wrapper {
|
|
294
|
+
width: 100%;
|
|
295
|
+
overflow-x: auto;
|
|
296
|
+
overflow-y: hidden;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.component {
|
|
300
|
+
border: 1px solid var(--border);
|
|
301
|
+
background: var(--card);
|
|
302
|
+
padding: 1.5rem;
|
|
303
|
+
margin-bottom: 1.5rem;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.tree-line {
|
|
307
|
+
font-family: "Fira Code", monospace;
|
|
308
|
+
font-size: 0.85rem;
|
|
309
|
+
white-space: pre;
|
|
310
|
+
color: var(--muted);
|
|
311
|
+
line-height: 1.8;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.branch {
|
|
315
|
+
color: var(--accent);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.pipe {
|
|
319
|
+
color: var(--border);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.file-badge {
|
|
323
|
+
background: var(--file-badge-bg);
|
|
324
|
+
color: var(--file-badge-text);
|
|
325
|
+
padding: 0.15rem 0.5rem;
|
|
326
|
+
font-size: 0.8rem;
|
|
327
|
+
font-family: "Fira Code", monospace;
|
|
328
|
+
display: inline-block;
|
|
329
|
+
margin-left: 0.25rem;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.table-container {
|
|
333
|
+
border: 1px solid var(--border);
|
|
334
|
+
margin: 1.5rem 0;
|
|
335
|
+
overflow-x: auto;
|
|
336
|
+
background: var(--card);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
table {
|
|
340
|
+
width: 100%;
|
|
341
|
+
border-collapse: collapse;
|
|
342
|
+
font-size: 0.9rem;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
th, td {
|
|
346
|
+
border-bottom: 1px solid var(--border);
|
|
347
|
+
padding: 0.75rem 1rem;
|
|
348
|
+
text-align: left;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
th {
|
|
352
|
+
background: var(--bg);
|
|
353
|
+
color: var(--muted);
|
|
354
|
+
text-transform: uppercase;
|
|
355
|
+
font-size: 0.8rem;
|
|
356
|
+
letter-spacing: 0.05em;
|
|
357
|
+
font-weight: 400;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
tr:last-child td {
|
|
361
|
+
border-bottom: none;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
code {
|
|
365
|
+
font-family: "Fira Code", monospace;
|
|
366
|
+
background: var(--code-bg);
|
|
367
|
+
padding: 0.2rem 0.4rem;
|
|
368
|
+
font-size: 0.9em;
|
|
369
|
+
color: var(--accent);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.list-item {
|
|
373
|
+
margin-bottom: 0.5rem;
|
|
374
|
+
padding-left: 1.5rem;
|
|
375
|
+
position: relative;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.list-item::before {
|
|
379
|
+
content: "";
|
|
380
|
+
position: absolute;
|
|
381
|
+
left: 0.5rem;
|
|
382
|
+
top: 0.7rem;
|
|
383
|
+
width: 4px;
|
|
384
|
+
height: 4px;
|
|
385
|
+
background: var(--accent);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
p {
|
|
389
|
+
margin-bottom: 1rem;
|
|
390
|
+
color: var(--muted);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
a {
|
|
394
|
+
color: var(--accent);
|
|
395
|
+
text-decoration: none;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
a:hover {
|
|
399
|
+
text-decoration: underline;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
mark {
|
|
403
|
+
background: #ffd33d;
|
|
404
|
+
color: #000;
|
|
405
|
+
padding: 0.1rem 0.3rem;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.mermaid {
|
|
409
|
+
background: transparent;
|
|
410
|
+
display: flex;
|
|
411
|
+
justify-content: center;
|
|
412
|
+
align-items: center;
|
|
413
|
+
min-height: 200px;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.mermaid svg {
|
|
417
|
+
max-width: 100%;
|
|
418
|
+
height: auto;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.error-message {
|
|
422
|
+
color: #f85149;
|
|
423
|
+
padding: 1rem;
|
|
424
|
+
background: rgba(248, 81, 73, 0.1);
|
|
425
|
+
border: 1px solid rgba(248, 81, 73, 0.3);
|
|
426
|
+
border-radius: 4px;
|
|
427
|
+
font-family: "Fira Code", monospace;
|
|
428
|
+
font-size: 0.9rem;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
@media print {
|
|
432
|
+
.controls {
|
|
433
|
+
display: none;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.container {
|
|
437
|
+
max-width: 100%;
|
|
438
|
+
padding: 0;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.component {
|
|
442
|
+
page-break-inside: avoid;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
@media (max-width: 768px) {
|
|
447
|
+
.graphs-grid {
|
|
448
|
+
grid-template-columns: 1fr;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.controls {
|
|
452
|
+
flex-direction: column;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
</style>
|
|
103
456
|
</head>
|
|
457
|
+
|
|
104
458
|
<body>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
459
|
+
<div class="container">
|
|
460
|
+
|
|
461
|
+
<div class="controls">
|
|
462
|
+
<input id="search" placeholder="${lang === 'fr' ? 'Rechercher dans la documentation...' : 'Search in documentation...'}" type="text">
|
|
463
|
+
<button onclick="toggleTheme()">
|
|
464
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
465
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/>
|
|
466
|
+
</svg>
|
|
467
|
+
${lang === 'fr' ? 'Thème' : 'Theme'}
|
|
468
|
+
</button>
|
|
469
|
+
<button onclick="window.print()">
|
|
470
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
471
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"/>
|
|
472
|
+
</svg>
|
|
473
|
+
${lang === 'fr' ? 'Exporter PDF' : 'Export PDF'}
|
|
474
|
+
</button>
|
|
475
|
+
</div>
|
|
111
476
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
477
|
+
<h2>${lang === 'fr' ? 'Architecture du projet' : 'Project Architecture'}</h2>
|
|
478
|
+
<div class="graphs-grid">
|
|
479
|
+
<div class="graph-container">
|
|
480
|
+
<div class="graph-title">${lang === 'fr' ? 'Graphe des dépendances' : 'Dependency Graph'}</div>
|
|
481
|
+
<div class="mermaid-wrapper">
|
|
482
|
+
<pre class="mermaid">
|
|
483
|
+
${cleanMermaidGraph}
|
|
484
|
+
</pre>
|
|
116
485
|
</div>
|
|
486
|
+
</div>
|
|
487
|
+
|
|
488
|
+
<div class="graph-container">
|
|
489
|
+
<div class="graph-title">${lang === 'fr' ? 'Structure du projet' : 'Project Structure'}</div>
|
|
490
|
+
<div class="mermaid-wrapper">
|
|
491
|
+
<pre class="mermaid">
|
|
492
|
+
${hierarchyGraph}
|
|
493
|
+
</pre>
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
</div>
|
|
117
497
|
|
|
118
|
-
|
|
498
|
+
<div class="graph-container" style="margin-bottom: 3rem;">
|
|
499
|
+
<div class="graph-title">${lang === 'fr' ? 'Flux de données' : 'Data Flow'}</div>
|
|
500
|
+
<div class="mermaid-wrapper">
|
|
501
|
+
<pre class="mermaid">
|
|
502
|
+
${dataFlowGraph}
|
|
503
|
+
</pre>
|
|
119
504
|
</div>
|
|
505
|
+
</div>
|
|
120
506
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
507
|
+
<div id="content-area">${htmlResult}</div>
|
|
508
|
+
|
|
509
|
+
</div>
|
|
510
|
+
|
|
511
|
+
<script>
|
|
512
|
+
function toggleTheme() {
|
|
513
|
+
const root = document.documentElement;
|
|
514
|
+
const next = root.dataset.theme === "dark" ? "light" : "dark";
|
|
515
|
+
root.dataset.theme = next;
|
|
516
|
+
localStorage.setItem("theme", next);
|
|
517
|
+
location.reload();
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
document.documentElement.dataset.theme = localStorage.getItem("theme") || "dark";
|
|
521
|
+
|
|
522
|
+
const originalHTML = document.getElementById("content-area").innerHTML;
|
|
523
|
+
|
|
524
|
+
document.getElementById("search").addEventListener("input", e => {
|
|
525
|
+
const term = e.target.value.trim();
|
|
526
|
+
const container = document.getElementById("content-area");
|
|
527
|
+
|
|
528
|
+
if (!term) {
|
|
529
|
+
container.innerHTML = originalHTML;
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
130
532
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
533
|
+
const regex = new RegExp("(" + term.replace(/[.*+?^\${}()|[\\]\\\\]/g, "\\\\$&") + ")", "gi");
|
|
534
|
+
container.innerHTML = originalHTML.replace(regex, "<mark>$1</mark>");
|
|
535
|
+
|
|
536
|
+
const first = container.querySelector("mark");
|
|
537
|
+
if (first) first.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// Gestion d'erreur Mermaid
|
|
541
|
+
window.addEventListener('load', () => {
|
|
542
|
+
setTimeout(() => {
|
|
543
|
+
document.querySelectorAll('.mermaid').forEach(el => {
|
|
544
|
+
if (!el.querySelector('svg') && !el.querySelector('.error-message')) {
|
|
545
|
+
const errorMsg = document.createElement('div');
|
|
546
|
+
errorMsg.className = 'error-message';
|
|
547
|
+
errorMsg.textContent = '${lang === 'fr' ? 'Erreur de rendu du graphe' : 'Graph rendering error'}';
|
|
548
|
+
el.appendChild(errorMsg);
|
|
549
|
+
}
|
|
136
550
|
});
|
|
137
|
-
|
|
551
|
+
}, 3000);
|
|
552
|
+
});
|
|
553
|
+
</script>
|
|
554
|
+
|
|
138
555
|
</body>
|
|
139
556
|
</html>`;
|
|
140
557
|
|
|
141
558
|
writeFileSync(path.join(docsDir, "report.html"), html);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function sanitizeMermaidGraph(graph: string): string {
|
|
562
|
+
// Nettoyer les caractères problématiques
|
|
563
|
+
let cleaned = graph
|
|
564
|
+
.replace(/[""]/g, '"')
|
|
565
|
+
.replace(/['']/g, "'")
|
|
566
|
+
.replace(/\u00A0/g, ' ') // Espaces insécables
|
|
567
|
+
.trim();
|
|
568
|
+
|
|
569
|
+
// Vérifier que le graphe a une déclaration valide
|
|
570
|
+
if (!cleaned.match(/^(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|gantt|pie|erDiagram)/)) {
|
|
571
|
+
cleaned = "graph TD\n " + cleaned;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Limiter la complexité si trop de nœuds
|
|
575
|
+
const lines = cleaned.split('\n');
|
|
576
|
+
if (lines.length > 50) {
|
|
577
|
+
cleaned = lines.slice(0, 50).join('\n') + '\n More["..."]';
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return cleaned;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function generateHierarchyGraph(tree: TreeStructure, maxDepth = 3): string {
|
|
584
|
+
let graph = "graph TD\n";
|
|
585
|
+
let nodeId = 0;
|
|
586
|
+
const nodeMap = new Map<string, string>();
|
|
587
|
+
|
|
588
|
+
function sanitizeLabel(label: string): string {
|
|
589
|
+
return label.replace(/["\[\]]/g, '');
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function traverse(obj: TreeStructure, parentId: string | null, depth: number, prefix = ""): void {
|
|
593
|
+
if (depth > maxDepth) return;
|
|
594
|
+
|
|
595
|
+
const entries = Object.entries(obj).slice(0, 10); // Limiter pour lisibilité
|
|
596
|
+
|
|
597
|
+
entries.forEach(([key, value]) => {
|
|
598
|
+
const currentId = `node${nodeId++}`;
|
|
599
|
+
const isFolder = value && typeof value === 'object' && Object.keys(value).length > 0;
|
|
600
|
+
const label = sanitizeLabel(isFolder ? `${key}/` : key);
|
|
601
|
+
|
|
602
|
+
nodeMap.set(currentId, label);
|
|
603
|
+
graph += ` ${currentId}["${label}"]\n`;
|
|
604
|
+
|
|
605
|
+
if (parentId) {
|
|
606
|
+
graph += ` ${parentId} --> ${currentId}\n`;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (isFolder && value && depth < maxDepth) {
|
|
610
|
+
traverse(value, currentId, depth + 1, prefix + key + "/");
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
try {
|
|
616
|
+
traverse(tree, null, 0);
|
|
617
|
+
return graph || getDefaultHierarchyGraph();
|
|
618
|
+
} catch (e) {
|
|
619
|
+
console.error("Error generating hierarchy graph:", e);
|
|
620
|
+
return getDefaultHierarchyGraph();
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function getDefaultHierarchyGraph(): string {
|
|
625
|
+
return `graph TD
|
|
626
|
+
A["Root"] --> B["src"]
|
|
627
|
+
A --> C["config"]
|
|
628
|
+
B --> D["components"]
|
|
629
|
+
B --> E["utils"]
|
|
630
|
+
B --> F["services"]`;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function generateDataFlowGraph(): string {
|
|
634
|
+
return `graph LR
|
|
635
|
+
A["Input"] --> B["Analyzer"]
|
|
636
|
+
B --> C["Parser"]
|
|
637
|
+
C --> D["Generator"]
|
|
638
|
+
D --> E["Exporter"]
|
|
639
|
+
E --> F["Output"]`;
|
|
142
640
|
}
|