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.
Files changed (43) hide show
  1. package/package.json +2 -2
  2. package/src/ai/analyzer.ts +6 -2
  3. package/src/analyzers/languageAnalyzer.ts +580 -0
  4. package/src/cache/cacheManager.ts +179 -0
  5. package/src/cli/index.ts +307 -23
  6. package/src/commands/generateSummary.ts +129 -39
  7. package/src/commands/run.ts +312 -75
  8. package/src/exporters/html.ts +604 -106
  9. package/.env +0 -1
  10. package/dist/ai/analyzer.d.ts +0 -3
  11. package/dist/ai/analyzer.d.ts.map +0 -1
  12. package/dist/ai/analyzer.js +0 -42
  13. package/dist/ai/analyzer.js.map +0 -1
  14. package/dist/cli/index.d.ts +0 -3
  15. package/dist/cli/index.d.ts.map +0 -1
  16. package/dist/cli/index.js +0 -59
  17. package/dist/cli/index.js.map +0 -1
  18. package/dist/commands/classify.d.ts +0 -2
  19. package/dist/commands/classify.d.ts.map +0 -1
  20. package/dist/commands/classify.js +0 -37
  21. package/dist/commands/classify.js.map +0 -1
  22. package/dist/commands/generateSummary.d.ts +0 -5
  23. package/dist/commands/generateSummary.d.ts.map +0 -1
  24. package/dist/commands/generateSummary.js +0 -54
  25. package/dist/commands/generateSummary.js.map +0 -1
  26. package/dist/commands/run.d.ts +0 -2
  27. package/dist/commands/run.d.ts.map +0 -1
  28. package/dist/commands/run.js +0 -142
  29. package/dist/commands/run.js.map +0 -1
  30. package/dist/exporters/html.d.ts +0 -2
  31. package/dist/exporters/html.d.ts.map +0 -1
  32. package/dist/exporters/html.js +0 -148
  33. package/dist/exporters/html.js.map +0 -1
  34. package/dist/index.d.ts +0 -2
  35. package/dist/index.d.ts.map +0 -1
  36. package/dist/index.js +0 -2
  37. package/dist/index.js.map +0 -1
  38. package/docs/docs-to-dev.md +0 -111
  39. package/docs/report.html +0 -90
  40. package/docs/report.pdf +0 -0
  41. package/docs/summary.md +0 -16
  42. package/src/commands/classify.ts +0 -40
  43. package/src/index.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docstodev",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "Solution d’automatisation de documentation technique intelligente avec IA",
5
5
  "type": "module",
6
6
  "main": "dist/cli/index.js",
@@ -36,4 +36,4 @@
36
36
  "globby": "^14.0.2",
37
37
  "puppeteer": "^24.1.1"
38
38
  }
39
- }
39
+ }
@@ -3,7 +3,9 @@
3
3
  import "dotenv/config"; // Pour lire GROQ_API_KEY dans ton .env
4
4
 
5
5
  export async function askAI(technicalContext: string) {
6
- const GROQ_API_KEY = process.env.GROQ_API_KEY;
6
+
7
+ // clé par défaut gratuite
8
+ const GROQ_API_KEY = process.env.GROQ_API_KEY || "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";
7
9
 
8
10
  if (!GROQ_API_KEY) {
9
11
  return "⚠️ Erreur : Clé API Groq manquante dans le fichier .env";
@@ -17,7 +19,9 @@ Instructions :
17
19
  - Vous allez recevoir une liste de fichiers, leurs rôles et leurs exports.
18
20
  - Pour chaque fichier, rédigez UNE SEULE phrase concise expliquant sa responsabilité métier.
19
21
  - Soyez pro, mais gardez votre touche amicale et votre pointe d'humour du Congo-Brazzaville.
20
- - Ne mentionnez pas de code technique (pas de "il y a une fonction X"), expliquez le BUT du fichier.
22
+ - mentionnez les imports, technique ( "il y a une fonction X"), expliquez le BUT du fichier.
23
+ - selon le nombre d'occurance précisez les couleurs qui reviennen souvent eu suggérez un design systèm , pallette de couleur.
24
+ - donnez une très bref description du but de l'ensemble du projet.
21
25
  `;
22
26
 
23
27
  try {
@@ -0,0 +1,580 @@
1
+ export interface LanguageAnalyzer {
2
+ extensions: string[];
3
+ analyzeFile(content: string): FileAnalysisResult;
4
+ }
5
+
6
+ export interface FileAnalysisResult {
7
+ functions: string[];
8
+ classes: string[];
9
+ types: string[];
10
+ imports: Array<{ name: string; type: string; usage?: string | undefined }>;
11
+ exports: string[];
12
+ structure?: Record<string, any>; // Pour les fichiers non-standards
13
+ }
14
+
15
+ // Analyseur TypeScript/JavaScript
16
+ class TSJSAnalyzer implements LanguageAnalyzer {
17
+ extensions = [".ts", ".js", ".tsx", ".jsx", ".mjs", ".cjs"];
18
+
19
+ analyzeFile(content: string): FileAnalysisResult {
20
+ const exports: string[] = [];
21
+ const exportRegex = /export\s+(?:default\s+)?(?:async\s+)?(?:function|const|let|class|type|interface|enum)\s+([a-zA-Z0-9_]+)/g;
22
+ let match: RegExpExecArray | null;
23
+ while ((match = exportRegex.exec(content)) !== null) {
24
+ if (match[1]) exports.push(match[1]);
25
+ }
26
+
27
+ const functions: string[] = [];
28
+ const funcRegex = /(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z0-9_]+)/g;
29
+ funcRegex.lastIndex = 0;
30
+ while ((match = funcRegex.exec(content)) !== null) {
31
+ if (match[1]) functions.push(match[1]);
32
+ }
33
+
34
+ const arrowRegex = /(?:export\s+)?const\s+([a-zA-Z0-9_]+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>/g;
35
+ while ((match = arrowRegex.exec(content)) !== null) {
36
+ if (match[1]) functions.push(match[1]);
37
+ }
38
+
39
+ const classes: string[] = [];
40
+ const classRegex = /(?:export\s+)?(?:abstract\s+)?class\s+([a-zA-Z0-9_]+)/g;
41
+ while ((match = classRegex.exec(content)) !== null) {
42
+ if (match[1]) classes.push(match[1]);
43
+ }
44
+
45
+ const types: string[] = [];
46
+ const typeRegex = /(?:export\s+)?(?:type|interface)\s+([a-zA-Z0-9_]+)/g;
47
+ while ((match = typeRegex.exec(content)) !== null) {
48
+ if (match[1]) types.push(match[1]);
49
+ }
50
+
51
+ const imports: Array<{ name: string; type: string; usage?: string | undefined }> = [];
52
+ const lines = content.split(/\r?\n/);
53
+ const importLines = lines.filter(l => l.trim().startsWith("import "));
54
+
55
+ importLines.forEach(line => {
56
+ const fromMatch = line.match(/from ['"]([^'"]+)['"]/);
57
+ if (fromMatch?.[1]) {
58
+ const name = fromMatch[1];
59
+ const type = name.startsWith('.') ? 'Interne' : (name.startsWith('node:') ? 'Node.js' : 'NPM');
60
+ const importedMatch = line.match(/import\s+(?:\{([^}]+)\}|(\w+))/);
61
+ let usage: string | undefined = undefined;
62
+ if (importedMatch) {
63
+ const items = importedMatch[1] || importedMatch[2];
64
+ usage = items?.trim().split(',').map(s => s.trim()).join(', ');
65
+ }
66
+ imports.push({ name, type, usage });
67
+ }
68
+ });
69
+
70
+ return { functions, classes, types, imports, exports };
71
+ }
72
+ }
73
+
74
+ // Analyseur Python
75
+ class PythonAnalyzer implements LanguageAnalyzer {
76
+ extensions = [".py"];
77
+
78
+ analyzeFile(content: string): FileAnalysisResult {
79
+ const functions: string[] = [];
80
+ const funcRegex = /def\s+([a-zA-Z0-9_]+)\s*\(/g;
81
+ let match: RegExpExecArray | null;
82
+ while ((match = funcRegex.exec(content)) !== null) {
83
+ if (match[1] && !match[1].startsWith('__')) functions.push(match[1]);
84
+ }
85
+
86
+ const classes: string[] = [];
87
+ const classRegex = /class\s+([a-zA-Z0-9_]+)(?:\(|:)/g;
88
+ while ((match = classRegex.exec(content)) !== null) {
89
+ if (match[1]) classes.push(match[1]);
90
+ }
91
+
92
+ const imports: Array<{ name: string; type: string; usage?: string | undefined }> = [];
93
+ const lines = content.split(/\r?\n/);
94
+
95
+ lines.forEach(line => {
96
+ const importMatch = line.match(/^import\s+([a-zA-Z0-9_., ]+)/);
97
+ const fromMatch = line.match(/^from\s+([a-zA-Z0-9_.]+)\s+import\s+(.+)/);
98
+
99
+ if (importMatch?.[1]) {
100
+ const modules = importMatch[1].split(',').map(m => m.trim());
101
+ modules.forEach(mod => {
102
+ const type = mod.startsWith('.') ? 'Interne' : 'PyPI';
103
+ imports.push({ name: mod, type });
104
+ });
105
+ } else if (fromMatch?.[1]) {
106
+ const moduleName = fromMatch[1];
107
+ const imported = fromMatch[2]?.trim();
108
+ const type = moduleName.startsWith('.') ? 'Interne' : 'PyPI';
109
+ imports.push({ name: moduleName, type, usage: imported });
110
+ }
111
+ });
112
+
113
+ return { functions, classes, types: [], imports, exports: [] };
114
+ }
115
+ }
116
+
117
+ // Analyseur Java
118
+ class JavaAnalyzer implements LanguageAnalyzer {
119
+ extensions = [".java"];
120
+
121
+ analyzeFile(content: string): FileAnalysisResult {
122
+ const classes: string[] = [];
123
+ const classRegex = /(?:public\s+)?(?:abstract\s+)?class\s+([a-zA-Z0-9_]+)/g;
124
+ let match: RegExpExecArray | null;
125
+ while ((match = classRegex.exec(content)) !== null) {
126
+ if (match[1]) classes.push(match[1]);
127
+ }
128
+
129
+ const interfaceRegex = /(?:public\s+)?interface\s+([a-zA-Z0-9_]+)/g;
130
+ while ((match = interfaceRegex.exec(content)) !== null) {
131
+ if (match[1]) classes.push(match[1]);
132
+ }
133
+
134
+ const functions: string[] = [];
135
+ const methodRegex = /(?:public|private|protected)\s+(?:static\s+)?(?:\w+(?:<[^>]+>)?)\s+([a-zA-Z0-9_]+)\s*\(/g;
136
+ while ((match = methodRegex.exec(content)) !== null) {
137
+ if (match[1]) functions.push(match[1]);
138
+ }
139
+
140
+ const imports: Array<{ name: string; type: string; usage?: string | undefined }> = [];
141
+ const lines = content.split(/\r?\n/);
142
+
143
+ lines.forEach(line => {
144
+ const importMatch = line.match(/^import\s+([a-zA-Z0-9_.]+);/);
145
+ if (importMatch?.[1]) {
146
+ const name = importMatch[1];
147
+ const type = name.startsWith('java.') || name.startsWith('javax.') ? 'JDK' : 'Maven';
148
+ imports.push({ name, type });
149
+ }
150
+ });
151
+
152
+ return { functions, classes, types: [], imports, exports: [] };
153
+ }
154
+ }
155
+
156
+ // Analyseur C#
157
+ class CSharpAnalyzer implements LanguageAnalyzer {
158
+ extensions = [".cs"];
159
+
160
+ analyzeFile(content: string): FileAnalysisResult {
161
+ const classes: string[] = [];
162
+ const classRegex = /(?:public\s+)?(?:abstract\s+)?(?:partial\s+)?class\s+([a-zA-Z0-9_]+)/g;
163
+ let match: RegExpExecArray | null;
164
+ while ((match = classRegex.exec(content)) !== null) {
165
+ if (match[1]) classes.push(match[1]);
166
+ }
167
+
168
+ const interfaceRegex = /(?:public\s+)?interface\s+([a-zA-Z0-9_]+)/g;
169
+ while ((match = interfaceRegex.exec(content)) !== null) {
170
+ if (match[1]) classes.push(match[1]);
171
+ }
172
+
173
+ const functions: string[] = [];
174
+ const methodRegex = /(?:public|private|protected|internal)\s+(?:static\s+)?(?:async\s+)?(?:\w+(?:<[^>]+>)?)\s+([a-zA-Z0-9_]+)\s*\(/g;
175
+ while ((match = methodRegex.exec(content)) !== null) {
176
+ if (match[1]) functions.push(match[1]);
177
+ }
178
+
179
+ const imports: Array<{ name: string; type: string; usage?: string | undefined }> = [];
180
+ const lines = content.split(/\r?\n/);
181
+
182
+ lines.forEach(line => {
183
+ const usingMatch = line.match(/^using\s+([a-zA-Z0-9_.]+);/);
184
+ if (usingMatch?.[1]) {
185
+ const name = usingMatch[1];
186
+ const type = name.startsWith('System.') ? '.NET' : 'NuGet';
187
+ imports.push({ name, type });
188
+ }
189
+ });
190
+
191
+ return { functions, classes, types: [], imports, exports: [] };
192
+ }
193
+ }
194
+
195
+ // Analyseur Go
196
+ class GoAnalyzer implements LanguageAnalyzer {
197
+ extensions = [".go"];
198
+
199
+ analyzeFile(content: string): FileAnalysisResult {
200
+ const functions: string[] = [];
201
+ const funcRegex = /func\s+(?:\([^)]+\)\s+)?([a-zA-Z0-9_]+)\s*\(/g;
202
+ let match: RegExpExecArray | null;
203
+ while ((match = funcRegex.exec(content)) !== null) {
204
+ if (match[1]) functions.push(match[1]);
205
+ }
206
+
207
+ const types: string[] = [];
208
+ const typeRegex = /type\s+([a-zA-Z0-9_]+)\s+(?:struct|interface)/g;
209
+ while ((match = typeRegex.exec(content)) !== null) {
210
+ if (match[1]) types.push(match[1]);
211
+ }
212
+
213
+ const imports: Array<{ name: string; type: string; usage?: string | undefined }> = [];
214
+ const lines = content.split(/\r?\n/);
215
+
216
+ lines.forEach(line => {
217
+ const importMatch = line.match(/^\s*"([^"]+)"/);
218
+ if (importMatch?.[1]) {
219
+ const name = importMatch[1];
220
+ const type = name.includes('.') ? 'External' : 'Standard';
221
+ imports.push({ name, type });
222
+ }
223
+ });
224
+
225
+ return { functions, classes: [], types, imports, exports: [] };
226
+ }
227
+ }
228
+
229
+ // Analyseur Rust
230
+ class RustAnalyzer implements LanguageAnalyzer {
231
+ extensions = [".rs"];
232
+
233
+ analyzeFile(content: string): FileAnalysisResult {
234
+ const functions: string[] = [];
235
+ const funcRegex = /(?:pub\s+)?fn\s+([a-zA-Z0-9_]+)/g;
236
+ let match: RegExpExecArray | null;
237
+ while ((match = funcRegex.exec(content)) !== null) {
238
+ if (match[1]) functions.push(match[1]);
239
+ }
240
+
241
+ const types: string[] = [];
242
+ const structRegex = /(?:pub\s+)?struct\s+([a-zA-Z0-9_]+)/g;
243
+ while ((match = structRegex.exec(content)) !== null) {
244
+ if (match[1]) types.push(match[1]);
245
+ }
246
+
247
+ const enumRegex = /(?:pub\s+)?enum\s+([a-zA-Z0-9_]+)/g;
248
+ while ((match = enumRegex.exec(content)) !== null) {
249
+ if (match[1]) types.push(match[1]);
250
+ }
251
+
252
+ const imports: Array<{ name: string; type: string; usage?: string | undefined }> = [];
253
+ const lines = content.split(/\r?\n/);
254
+
255
+ lines.forEach(line => {
256
+ const useMatch = line.match(/^use\s+([a-zA-Z0-9_:]+)(?:::\{([^}]+)\})?/);
257
+ if (useMatch?.[1]) {
258
+ const name = useMatch[1];
259
+ const type = name.startsWith('std::') || name.startsWith('core::') ? 'Standard' : 'Crate';
260
+ const usage = useMatch[2];
261
+ imports.push({ name, type, usage });
262
+ }
263
+ });
264
+
265
+ return { functions, classes: [], types, imports, exports: [] };
266
+ }
267
+ }
268
+
269
+ // Analyseur HTML
270
+ class HTMLAnalyzer implements LanguageAnalyzer {
271
+ extensions = [".html", ".htm"];
272
+
273
+ analyzeFile(content: string): FileAnalysisResult {
274
+ const structure: Record<string, any> = {
275
+ tags: new Set<string>(),
276
+ ids: [] as string[],
277
+ classes: [] as string[],
278
+ scripts: [] as string[],
279
+ styles: [] as string[]
280
+ };
281
+
282
+ // Extraire les balises
283
+ const tagRegex = /<(\w+)/g;
284
+ let match: RegExpExecArray | null;
285
+ while ((match = tagRegex.exec(content)) !== null) {
286
+ if (match[1]) structure.tags.add(match[1].toLowerCase());
287
+ }
288
+
289
+ // Extraire les IDs
290
+ const idRegex = /id=["']([^"']+)["']/g;
291
+ while ((match = idRegex.exec(content)) !== null) {
292
+ if (match[1]) structure.ids.push(match[1]);
293
+ }
294
+
295
+ // Extraire les classes
296
+ const classRegex = /class=["']([^"']+)["']/g;
297
+ while ((match = classRegex.exec(content)) !== null) {
298
+ if (match[1]) {
299
+ structure.classes.push(...match[1].split(/\s+/).filter(Boolean));
300
+ }
301
+ }
302
+
303
+ // Extraire les scripts externes
304
+ const scriptRegex = /<script[^>]+src=["']([^"']+)["']/g;
305
+ while ((match = scriptRegex.exec(content)) !== null) {
306
+ if (match[1]) structure.scripts.push(match[1]);
307
+ }
308
+
309
+ // Extraire les stylesheets externes
310
+ const linkRegex = /<link[^>]+href=["']([^"']+\.css)["']/g;
311
+ while ((match = linkRegex.exec(content)) !== null) {
312
+ if (match[1]) structure.styles.push(match[1]);
313
+ }
314
+
315
+ structure.tags = Array.from(structure.tags);
316
+
317
+ return {
318
+ functions: [],
319
+ classes: structure.classes,
320
+ types: [],
321
+ imports: [
322
+ ...structure.scripts.map((s : string) => ({ name: s, type: 'Script' })),
323
+ ...structure.styles.map((s:string) => ({ name: s, type: 'Stylesheet' }))
324
+ ],
325
+ exports: [],
326
+ structure
327
+ };
328
+ }
329
+ }
330
+
331
+ // Analyseur CSS/SCSS/SASS
332
+ class CSSAnalyzer implements LanguageAnalyzer {
333
+ extensions = [".css", ".scss", ".sass", ".less"];
334
+
335
+ analyzeFile(content: string): FileAnalysisResult {
336
+ const structure: Record<string, any> = {
337
+ selectors: [] as string[],
338
+ ids: [] as string[],
339
+ classes: [] as string[],
340
+ variables: [] as string[],
341
+ mixins: [] as string[],
342
+ imports: [] as string[]
343
+ };
344
+
345
+ // Classes CSS
346
+ const classRegex = /\.([a-zA-Z0-9_-]+)/g;
347
+ let match: RegExpExecArray | null;
348
+ while ((match = classRegex.exec(content)) !== null) {
349
+ if (match[1] && !structure.classes.includes(match[1])) {
350
+ structure.classes.push(match[1]);
351
+ }
352
+ }
353
+
354
+ // IDs CSS
355
+ const idRegex = /#([a-zA-Z0-9_-]+)/g;
356
+ while ((match = idRegex.exec(content)) !== null) {
357
+ if (match[1] && !structure.ids.includes(match[1])) {
358
+ structure.ids.push(match[1]);
359
+ }
360
+ }
361
+
362
+ // Variables CSS/SCSS
363
+ const varRegex = /(?:--|\$)([a-zA-Z0-9_-]+)/g;
364
+ while ((match = varRegex.exec(content)) !== null) {
365
+ if (match[1] && !structure.variables.includes(match[1])) {
366
+ structure.variables.push(match[1]);
367
+ }
368
+ }
369
+
370
+ // Mixins SCSS
371
+ const mixinRegex = /@mixin\s+([a-zA-Z0-9_-]+)/g;
372
+ while ((match = mixinRegex.exec(content)) !== null) {
373
+ if (match[1]) structure.mixins.push(match[1]);
374
+ }
375
+
376
+ // Imports
377
+ const importRegex = /@import\s+['"]([^'"]+)['"]/g;
378
+ while ((match = importRegex.exec(content)) !== null) {
379
+ if (match[1]) structure.imports.push(match[1]);
380
+ }
381
+
382
+ return {
383
+ functions: structure.mixins,
384
+ classes: structure.classes,
385
+ types: [],
386
+ imports: structure.imports.map((i:string) => ({ name: i, type: 'CSS' })),
387
+ exports: [],
388
+ structure
389
+ };
390
+ }
391
+ }
392
+
393
+ // Analyseur PHP
394
+ class PHPAnalyzer implements LanguageAnalyzer {
395
+ extensions = [".php"];
396
+
397
+ analyzeFile(content: string): FileAnalysisResult {
398
+ const functions: string[] = [];
399
+ const funcRegex = /function\s+([a-zA-Z0-9_]+)\s*\(/g;
400
+ let match: RegExpExecArray | null;
401
+ while ((match = funcRegex.exec(content)) !== null) {
402
+ if (match[1]) functions.push(match[1]);
403
+ }
404
+
405
+ const classes: string[] = [];
406
+ const classRegex = /class\s+([a-zA-Z0-9_]+)/g;
407
+ while ((match = classRegex.exec(content)) !== null) {
408
+ if (match[1]) classes.push(match[1]);
409
+ }
410
+
411
+ const imports: Array<{ name: string; type: string; usage?: string | undefined }> = [];
412
+ const useRegex = /use\s+([a-zA-Z0-9_\\]+)/g;
413
+ while ((match = useRegex.exec(content)) !== null) {
414
+ if (match[1]) {
415
+ imports.push({ name: match[1], type: 'Composer' });
416
+ }
417
+ }
418
+
419
+ const requireRegex = /(?:require|include)(?:_once)?\s*['"]([^'"]+)['"]/g;
420
+ while ((match = requireRegex.exec(content)) !== null) {
421
+ if (match[1]) {
422
+ imports.push({ name: match[1], type: 'Local' });
423
+ }
424
+ }
425
+
426
+ return { functions, classes, types: [], imports, exports: [] };
427
+ }
428
+ }
429
+
430
+ // Analyseur Ruby
431
+ class RubyAnalyzer implements LanguageAnalyzer {
432
+ extensions = [".rb"];
433
+
434
+ analyzeFile(content: string): FileAnalysisResult {
435
+ const functions: string[] = [];
436
+ const funcRegex = /def\s+([a-zA-Z0-9_?!]+)/g;
437
+ let match: RegExpExecArray | null;
438
+ while ((match = funcRegex.exec(content)) !== null) {
439
+ if (match[1]) functions.push(match[1]);
440
+ }
441
+
442
+ const classes: string[] = [];
443
+ const classRegex = /class\s+([a-zA-Z0-9_]+)/g;
444
+ while ((match = classRegex.exec(content)) !== null) {
445
+ if (match[1]) classes.push(match[1]);
446
+ }
447
+
448
+ const imports: Array<{ name: string; type: string; usage?: string | undefined }> = [];
449
+ const requireRegex = /require\s+['"]([^'"]+)['"]/g;
450
+ while ((match = requireRegex.exec(content)) !== null) {
451
+ if (match[1]) {
452
+ imports.push({ name: match[1], type: 'Gem' });
453
+ }
454
+ }
455
+
456
+ return { functions, classes, types: [], imports, exports: [] };
457
+ }
458
+ }
459
+
460
+ // Analyseur SQL
461
+ class SQLAnalyzer implements LanguageAnalyzer {
462
+ extensions = [".sql"];
463
+
464
+ analyzeFile(content: string): FileAnalysisResult {
465
+ const structure: Record<string, any> = {
466
+ tables: [] as string[],
467
+ procedures: [] as string[],
468
+ functions: [] as string[],
469
+ views: [] as string[]
470
+ };
471
+
472
+ const tableRegex = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?([a-zA-Z0-9_]+)/gi;
473
+ let match: RegExpExecArray | null;
474
+ while ((match = tableRegex.exec(content)) !== null) {
475
+ if (match[1]) structure.tables.push(match[1]);
476
+ }
477
+
478
+ const procRegex = /CREATE\s+(?:OR\s+REPLACE\s+)?PROCEDURE\s+([a-zA-Z0-9_]+)/gi;
479
+ while ((match = procRegex.exec(content)) !== null) {
480
+ if (match[1]) structure.procedures.push(match[1]);
481
+ }
482
+
483
+ const funcRegex = /CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+([a-zA-Z0-9_]+)/gi;
484
+ while ((match = funcRegex.exec(content)) !== null) {
485
+ if (match[1]) structure.functions.push(match[1]);
486
+ }
487
+
488
+ const viewRegex = /CREATE\s+(?:OR\s+REPLACE\s+)?VIEW\s+([a-zA-Z0-9_]+)/gi;
489
+ while ((match = viewRegex.exec(content)) !== null) {
490
+ if (match[1]) structure.views.push(match[1]);
491
+ }
492
+
493
+ return {
494
+ functions: structure.functions,
495
+ classes: structure.tables,
496
+ types: structure.views,
497
+ imports: [],
498
+ exports: [],
499
+ structure
500
+ };
501
+ }
502
+ }
503
+
504
+ // Analyseur par défaut pour tous les autres fichiers
505
+ class DefaultAnalyzer implements LanguageAnalyzer {
506
+ extensions = ["*"]; // Wildcard pour tous les fichiers
507
+
508
+ analyzeFile(content: string): FileAnalysisResult {
509
+ const lines = content.split(/\r?\n/);
510
+ const structure: Record<string, any> = {
511
+ lineCount: lines.length,
512
+ nonEmptyLines: lines.filter(l => l.trim().length > 0).length,
513
+ size: content.length,
514
+ hasCode: /[a-zA-Z0-9_]+\s*[=:({]/.test(content),
515
+ encoding: 'UTF-8'
516
+ };
517
+
518
+ // Détection basique de patterns communs
519
+ const patterns = {
520
+ functions: [] as string[],
521
+ variables: [] as string[],
522
+ comments: [] as string[]
523
+ };
524
+
525
+ // Détection générique de fonctions (pattern commun à beaucoup de langages)
526
+ const genericFuncRegex = /(?:function|func|def|fn|sub|procedure)\s+([a-zA-Z0-9_]+)/gi;
527
+ let match: RegExpExecArray | null;
528
+ while ((match = genericFuncRegex.exec(content)) !== null) {
529
+ if (match[1]) patterns.functions.push(match[1]);
530
+ }
531
+
532
+ structure.patterns = patterns;
533
+
534
+ return {
535
+ functions: patterns.functions,
536
+ classes: [],
537
+ types: [],
538
+ imports: [],
539
+ exports: [],
540
+ structure
541
+ };
542
+ }
543
+ }
544
+
545
+ // Registry des analyseurs
546
+ const analyzers: LanguageAnalyzer[] = [
547
+ new TSJSAnalyzer(),
548
+ new PythonAnalyzer(),
549
+ new JavaAnalyzer(),
550
+ new CSharpAnalyzer(),
551
+ new GoAnalyzer(),
552
+ new RustAnalyzer(),
553
+ new HTMLAnalyzer(),
554
+ new CSSAnalyzer(),
555
+ new PHPAnalyzer(),
556
+ new RubyAnalyzer(),
557
+ new SQLAnalyzer()
558
+ ];
559
+
560
+ const defaultAnalyzer = new DefaultAnalyzer();
561
+
562
+ export function getAnalyzer(filePath: string): LanguageAnalyzer {
563
+ const lastDotIndex = filePath.lastIndexOf('.');
564
+ if (lastDotIndex === -1) return defaultAnalyzer;
565
+
566
+ const ext = filePath.substring(lastDotIndex).toLowerCase();
567
+ return analyzers.find(a => a.extensions.includes(ext)) || defaultAnalyzer;
568
+ }
569
+
570
+ export function getSupportedExtensions(): string[] {
571
+ return analyzers.flatMap(a => a.extensions);
572
+ }
573
+
574
+ export function isExplicitlySupported(filePath: string): boolean {
575
+ const lastDotIndex = filePath.lastIndexOf('.');
576
+ if (lastDotIndex === -1) return false;
577
+
578
+ const ext = filePath.substring(lastDotIndex).toLowerCase();
579
+ return analyzers.some(a => a.extensions.includes(ext));
580
+ }