docstodev 1.1.1 → 2.0.1

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.
@@ -38,13 +38,12 @@ export function exportToHTML(
38
38
  const cells = processed.split("|").filter(c => c.trim());
39
39
  const isHeader = processed.includes("Module") || processed.includes("Type");
40
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>`;
41
+ htmlResult += `<tr>${cells.map(c =>
42
+ isHeader
43
+ ? `<th>${formatText(c.trim())}</th>`
44
+ : `<td>${formatText(c.trim())}</td>`
45
+ ).join("")
46
+ }</tr>`;
48
47
  continue;
49
48
  } else if (inTable) {
50
49
  htmlResult += "</table></div>";
@@ -59,7 +58,7 @@ export function exportToHTML(
59
58
  ) {
60
59
  const hasFile = processed.match(/\.(ts|js|py|java|cs|go|rs|tsx|jsx|html|css|php|rb|sql)$/);
61
60
  const fileMatch = hasFile ? processed.match(/([\w-]+\.(ts|js|py|java|cs|go|rs|tsx|jsx|html|css|php|rb|sql))/)?.[0] : null;
62
-
61
+
63
62
  let treeLine = line
64
63
  .replace(/├─/g, '<span class="branch">├─</span>')
65
64
  .replace(/└─/g, '<span class="branch">└─</span>')
@@ -109,6 +108,10 @@ export function exportToHTML(
109
108
  const hierarchyGraph = fileTree ? generateHierarchyGraph(fileTree) : getDefaultHierarchyGraph();
110
109
  const dataFlowGraph = generateDataFlowGraph();
111
110
 
111
+ // Extraire les sections spéciales de markdownContent
112
+ const projectGoalMatch = markdownContent.match(/(?:But du Projet|Project Goal)[:\s]*\n+([\s\S]*?)(?=\n#{2,}|$)/i);
113
+ const risksMatch = markdownContent.match(/(?:Risques Identifiés|Identified Risks)[:\s]*\n+([\s\S]*?)(?=\n#{2,}|$)/i);
114
+
112
115
  const html = `<!DOCTYPE html>
113
116
  <html lang="${lang}" data-theme="dark">
114
117
  <head>
@@ -303,10 +306,18 @@ h3 {
303
306
  margin-bottom: 1.5rem;
304
307
  }
305
308
 
309
+ .component p,
310
+ .component .list-item {
311
+ word-wrap: break-word;
312
+ overflow-wrap: break-word;
313
+ word-break: break-word;
314
+ }
315
+
306
316
  .tree-line {
307
317
  font-family: "Fira Code", monospace;
308
318
  font-size: 0.85rem;
309
- white-space: pre;
319
+ white-space: pre-wrap;
320
+ word-wrap: break-word;
310
321
  color: var(--muted);
311
322
  line-height: 1.8;
312
323
  }
@@ -340,12 +351,15 @@ table {
340
351
  width: 100%;
341
352
  border-collapse: collapse;
342
353
  font-size: 0.9rem;
354
+ table-layout: fixed;
343
355
  }
344
356
 
345
357
  th, td {
346
358
  border-bottom: 1px solid var(--border);
347
359
  padding: 0.75rem 1rem;
348
360
  text-align: left;
361
+ word-wrap: break-word;
362
+ overflow-wrap: break-word;
349
363
  }
350
364
 
351
365
  th {
@@ -367,6 +381,8 @@ code {
367
381
  padding: 0.2rem 0.4rem;
368
382
  font-size: 0.9em;
369
383
  color: var(--accent);
384
+ word-wrap: break-word;
385
+ overflow-wrap: break-word;
370
386
  }
371
387
 
372
388
  .list-item {
@@ -428,6 +444,133 @@ mark {
428
444
  font-size: 0.9rem;
429
445
  }
430
446
 
447
+ .project-goal-section {
448
+ background: linear-gradient(135deg, var(--accent) 0%, #7c3aed 100%);
449
+ padding: 2rem;
450
+ margin: 2rem 0;
451
+ border-radius: 8px;
452
+ border-left: 4px solid var(--accent);
453
+ }
454
+
455
+ .project-goal-section h2 {
456
+ color: #ffffff;
457
+ border-bottom: none;
458
+ margin: 0 0 1rem 0;
459
+ font-size: 1.8rem;
460
+ }
461
+
462
+ .project-goal-section p {
463
+ color: rgba(255, 255, 255, 0.95);
464
+ font-size: 1.1rem;
465
+ line-height: 1.8;
466
+ }
467
+
468
+ .risks-section {
469
+ background: var(--card);
470
+ border: 1px solid var(--border);
471
+ border-left: 4px solid #f85149;
472
+ padding: 1.5rem;
473
+ margin: 2rem 0;
474
+ }
475
+
476
+ .risks-section h3 {
477
+ color: #f85149;
478
+ margin-bottom: 1rem;
479
+ }
480
+
481
+ .risk-item {
482
+ padding: 0.75rem;
483
+ margin: 0.5rem 0;
484
+ border-radius: 4px;
485
+ display: flex;
486
+ align-items: flex-start;
487
+ gap: 0.75rem;
488
+ }
489
+
490
+ .risk-item.critical {
491
+ background: rgba(248, 81, 73, 0.1);
492
+ border-left: 3px solid #f85149;
493
+ }
494
+
495
+ .risk-item.medium {
496
+ background: rgba(251, 191, 36, 0.1);
497
+ border-left: 3px solid #fbbf24;
498
+ }
499
+
500
+ .risk-item.low {
501
+ background: rgba(34, 197, 94, 0.1);
502
+ border-left: 3px solid #22c55e;
503
+ }
504
+
505
+ .risk-icon {
506
+ font-size: 1.2rem;
507
+ flex-shrink: 0;
508
+ }
509
+
510
+ .recommendations-section {
511
+ background: var(--card);
512
+ border: 1px solid var(--border);
513
+ border-left: 4px solid #22c55e;
514
+ padding: 1.5rem;
515
+ margin: 2rem 0;
516
+ }
517
+
518
+ .recommendations-section h3 {
519
+ color: #22c55e;
520
+ margin-bottom: 1rem;
521
+ }
522
+
523
+ .recommendation-item {
524
+ padding: 0.75rem;
525
+ margin: 0.5rem 0;
526
+ background: rgba(34, 197, 94, 0.05);
527
+ border-radius: 4px;
528
+ padding-left: 2rem;
529
+ position: relative;
530
+ }
531
+
532
+ .recommendation-item::before {
533
+ content: "💡";
534
+ position: absolute;
535
+ left: 0.5rem;
536
+ top: 0.75rem;
537
+ }
538
+
539
+ .design-system-section {
540
+ background: var(--card);
541
+ border: 1px solid var(--border);
542
+ padding: 1.5rem;
543
+ margin: 2rem 0;
544
+ }
545
+
546
+ .color-palette {
547
+ display: flex;
548
+ gap: 1rem;
549
+ flex-wrap: wrap;
550
+ margin: 1rem 0;
551
+ }
552
+
553
+ .color-swatch {
554
+ display: flex;
555
+ flex-direction: column;
556
+ align-items: center;
557
+ gap: 0.5rem;
558
+ }
559
+
560
+ .color-box {
561
+ width: 60px;
562
+ height: 60px;
563
+ border-radius: 8px;
564
+ border: 2px solid var(--border);
565
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
566
+ }
567
+
568
+ .color-label {
569
+ font-size: 0.75rem;
570
+ font-family: "Fira Code", monospace;
571
+ color: var(--muted);
572
+ }
573
+
431
574
  @media print {
432
575
  .controls {
433
576
  display: none;
@@ -458,6 +601,15 @@ mark {
458
601
  <body>
459
602
  <div class="container">
460
603
 
604
+ ${projectGoalMatch ? `
605
+ <div class="project-goal-section">
606
+ <h2>🎯 ${lang === 'fr' ? 'But du Projet' : 'Project Goal'}</h2>
607
+ <p>${formatText(projectGoalMatch[1]?.trim() || "")}</p>
608
+ </div>
609
+ ` : ''}
610
+
611
+ ${risksMatch ? formatRisksSection(risksMatch[1] || "", lang) : ''}
612
+
461
613
  <div class="controls">
462
614
  <input id="search" placeholder="${lang === 'fr' ? 'Rechercher dans la documentation...' : 'Search in documentation...'}" type="text">
463
615
  <button onclick="toggleTheme()">
@@ -530,14 +682,13 @@ document.getElementById("search").addEventListener("input", e => {
530
682
  return;
531
683
  }
532
684
 
533
- const regex = new RegExp("(" + term.replace(/[.*+?^\${}()|[\\]\\\\]/g, "\\\\$&") + ")", "gi");
685
+ const regex = new RegExp("(" + term.replace(/[.*+?\\\${}()|[\\]\\\\]/g, "\\\\$&") + ")", "gi");
534
686
  container.innerHTML = originalHTML.replace(regex, "<mark>$1</mark>");
535
687
 
536
688
  const first = container.querySelector("mark");
537
689
  if (first) first.scrollIntoView({ behavior: "smooth", block: "center" });
538
690
  });
539
691
 
540
- // Gestion d'erreur Mermaid
541
692
  window.addEventListener('load', () => {
542
693
  setTimeout(() => {
543
694
  document.querySelectorAll('.mermaid').forEach(el => {
@@ -559,61 +710,124 @@ window.addEventListener('load', () => {
559
710
  }
560
711
 
561
712
  function sanitizeMermaidGraph(graph: string): string {
562
- // Nettoyer les caractères problématiques
563
713
  let cleaned = graph
564
714
  .replace(/[""]/g, '"')
565
715
  .replace(/['']/g, "'")
566
- .replace(/\u00A0/g, ' ') // Espaces insécables
716
+ .replace(/\u00A0/g, ' ')
567
717
  .trim();
568
-
569
- // Vérifier que le graphe a une déclaration valide
718
+
570
719
  if (!cleaned.match(/^(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|gantt|pie|erDiagram)/)) {
571
720
  cleaned = "graph TD\n " + cleaned;
572
721
  }
573
-
574
- // Limiter la complexité si trop de nœuds
722
+
575
723
  const lines = cleaned.split('\n');
576
- if (lines.length > 50) {
577
- cleaned = lines.slice(0, 50).join('\n') + '\n More["..."]';
724
+ let enrichedGraph = lines[0] + '\n';
725
+
726
+ for (let i = 1; i < Math.min(lines.length, 40); i++) {
727
+ const line = lines[i]?.trim();
728
+ if (!line || line.startsWith('classDef')) continue;
729
+
730
+ let styledLine = line;
731
+
732
+ if (line.includes('_js') || line.includes('.js"')) {
733
+ styledLine = line + ':::jsFileStyle';
734
+ } else if (line.includes('_ts') || line.includes('.ts"')) {
735
+ styledLine = line + ':::tsFileStyle';
736
+ } else if (line.includes('Analyzer') || line.includes('analyzer')) {
737
+ styledLine = line + ':::analyzerStyle';
738
+ } else if (line.includes('Component') || line.includes('component')) {
739
+ styledLine = line + ':::componentStyle';
740
+ } else if (line.match(/\(\)/)) {
741
+ styledLine = line + ':::functionStyle';
742
+ } else if (line.includes('Service') || line.includes('service')) {
743
+ styledLine = line + ':::serviceStyle';
744
+ } else if (line.includes('Config') || line.includes('config')) {
745
+ styledLine = line + ':::configStyle';
746
+ }
747
+
748
+ enrichedGraph += ' ' + styledLine + '\n';
578
749
  }
579
-
580
- return cleaned;
750
+
751
+ enrichedGraph += `
752
+ classDef jsFileStyle fill:#f7df1e,stroke:#c9b003,stroke-width:2px,color:#000
753
+ classDef tsFileStyle fill:#3178c6,stroke:#235a97,stroke-width:2px,color:#fff
754
+ classDef analyzerStyle fill:#8b5cf6,stroke:#6d28d9,stroke-width:2px,color:#fff
755
+ classDef componentStyle fill:#06b6d4,stroke:#0891b2,stroke-width:2px,color:#fff
756
+ classDef functionStyle fill:#ec4899,stroke:#be185d,stroke-width:2px,color:#fff
757
+ classDef serviceStyle fill:#10b981,stroke:#059669,stroke-width:2px,color:#fff
758
+ classDef configStyle fill:#6e7681,stroke:#484f58,stroke-width:2px,color:#fff`;
759
+
760
+ return enrichedGraph;
581
761
  }
582
762
 
583
- function generateHierarchyGraph(tree: TreeStructure, maxDepth = 3): string {
763
+ function generateHierarchyGraph(tree: TreeStructure, maxDepth: number = 4): string {
584
764
  let graph = "graph TD\n";
585
765
  let nodeId = 0;
586
- const nodeMap = new Map<string, string>();
587
-
766
+
588
767
  function sanitizeLabel(label: string): string {
589
- return label.replace(/["\[\]]/g, '');
768
+ return label.replace(/["\[\]]/g, '').replace(/\//g, '');
590
769
  }
591
-
592
- function traverse(obj: TreeStructure, parentId: string | null, depth: number, prefix = ""): void {
770
+
771
+ function getFileExtension(filename: string): string {
772
+ const parts = filename.split('.');
773
+ return parts.length > 1 ? parts[parts.length - 1] ?? "" : '';
774
+ }
775
+
776
+ function getNodeStyle(key: string, isFolder: boolean): string {
777
+ if (isFolder) {
778
+ return ':::folderStyle';
779
+ }
780
+ const ext = getFileExtension(key);
781
+ const styleMap: { [key: string]: string } = {
782
+ 'ts': ':::tsStyle',
783
+ 'tsx': ':::tsStyle',
784
+ 'js': ':::jsStyle',
785
+ 'jsx': ':::jsStyle',
786
+ 'json': ':::configStyle',
787
+ 'html': ':::htmlStyle',
788
+ 'css': ':::cssStyle',
789
+ 'md': ':::docStyle'
790
+ };
791
+ return styleMap[ext] || ':::fileStyle';
792
+ }
793
+
794
+ function traverse(obj: TreeStructure, parentId: string | null, depth: number): void {
593
795
  if (depth > maxDepth) return;
594
-
595
- const entries = Object.entries(obj).slice(0, 10); // Limiter pour lisibilité
596
-
796
+
797
+ const entries = Object.entries(obj);
798
+
597
799
  entries.forEach(([key, value]) => {
598
800
  const currentId = `node${nodeId++}`;
599
801
  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
-
802
+ const label = sanitizeLabel(key);
803
+ const displayLabel = isFolder ? `📁 ${label}` : `📄 ${label}`;
804
+ const style = getNodeStyle(key, isFolder || false);
805
+
806
+ graph += ` ${currentId}["${displayLabel}"]${style}\n`;
807
+
605
808
  if (parentId) {
606
809
  graph += ` ${parentId} --> ${currentId}\n`;
607
810
  }
608
-
811
+
609
812
  if (isFolder && value && depth < maxDepth) {
610
- traverse(value, currentId, depth + 1, prefix + key + "/");
813
+ traverse(value, currentId, depth + 1);
611
814
  }
612
815
  });
613
816
  }
614
-
817
+
615
818
  try {
616
819
  traverse(tree, null, 0);
820
+
821
+ graph += `
822
+ classDef folderStyle fill:#58a6ff,stroke:#1f6feb,stroke-width:2px,color:#fff
823
+ classDef tsStyle fill:#3178c6,stroke:#235a97,stroke-width:2px,color:#fff
824
+ classDef jsStyle fill:#f7df1e,stroke:#c9b003,stroke-width:2px,color:#000
825
+ classDef configStyle fill:#6e7681,stroke:#484f58,stroke-width:2px,color:#fff
826
+ classDef htmlStyle fill:#e34c26,stroke:#b83c1f,stroke-width:2px,color:#fff
827
+ classDef cssStyle fill:#264de4,stroke:#1b3ba3,stroke-width:2px,color:#fff
828
+ classDef docStyle fill:#22c55e,stroke:#16a34a,stroke-width:2px,color:#fff
829
+ classDef fileStyle fill:#8b949e,stroke:#6e7681,stroke-width:2px,color:#fff`;
830
+
617
831
  return graph || getDefaultHierarchyGraph();
618
832
  } catch (e) {
619
833
  console.error("Error generating hierarchy graph:", e);
@@ -623,18 +837,58 @@ function generateHierarchyGraph(tree: TreeStructure, maxDepth = 3): string {
623
837
 
624
838
  function getDefaultHierarchyGraph(): string {
625
839
  return `graph TD
626
- A["Root"] --> B["src"]
627
- A --> C["config"]
628
- B --> D["components"]
629
- B --> E["utils"]
630
- B --> F["services"]`;
840
+ A["📁 Root"]:::folderStyle --> B["📁 src"]:::folderStyle
841
+ A --> C["📁 config"]:::folderStyle
842
+ B --> D["📁 components"]:::folderStyle
843
+ B --> E["📁 utils"]:::folderStyle
844
+ B --> F["📁 services"]:::folderStyle
845
+
846
+ classDef folderStyle fill:#58a6ff,stroke:#1f6feb,stroke-width:2px,color:#fff`;
631
847
  }
632
848
 
633
849
  function generateDataFlowGraph(): string {
634
850
  return `graph LR
635
- A["Input"] --> B["Analyzer"]
636
- B --> C["Parser"]
637
- C --> D["Generator"]
638
- D --> E["Exporter"]
639
- E --> F["Output"]`;
851
+ A["📥 Input<br/>(fichiers source)"]:::inputStyle --> B["🔍 Analyzer<br/>(analyse code)"]:::analyzerStyle
852
+ B --> C["📊 Parser<br/>(extraction données)"]:::parserStyle
853
+ C --> D["⚙️ Generator<br/>(génération docs)"]:::generatorStyle
854
+ D --> E["📤 Exporter<br/>(export HTML/PDF)"]:::exporterStyle
855
+ E --> F["Output<br/>(documentation)"]:::outputStyle
856
+
857
+ classDef inputStyle fill:#3b82f6,stroke:#1d4ed8,stroke-width:2px,color:#fff
858
+ classDef analyzerStyle fill:#8b5cf6,stroke:#6d28d9,stroke-width:2px,color:#fff
859
+ classDef parserStyle fill:#ec4899,stroke:#be185d,stroke-width:2px,color:#fff
860
+ classDef generatorStyle fill:#f59e0b,stroke:#d97706,stroke-width:2px,color:#fff
861
+ classDef exporterStyle fill:#10b981,stroke:#059669,stroke-width:2px,color:#fff
862
+ classDef outputStyle fill:#22c55e,stroke:#16a34a,stroke-width:2px,color:#fff`;
863
+ }
864
+
865
+ function formatRisksSection(risksContent: string, lang: "fr" | "en"): string {
866
+ const criticalMatches = risksContent.match(/🔴[^\n]+/g) || [];
867
+ const mediumMatches = risksContent.match(/🟡[^\n]+/g) || [];
868
+ const lowMatches = risksContent.match(/🟢[^\n]+/g) || [];
869
+
870
+ if (criticalMatches.length === 0 && mediumMatches.length === 0 && lowMatches.length === 0) {
871
+ return '';
872
+ }
873
+
874
+ let html = `
875
+ <div class="risks-section">
876
+ <h3>⚠️ ${lang === 'fr' ? 'Risques Identifiés' : 'Identified Risks'}</h3>
877
+ `;
878
+
879
+ criticalMatches.forEach(risk => {
880
+ html += `<div class="risk-item critical"><span class="risk-icon">🔴</span><span>${risk.replace(/🔴\s*/, '')}</span></div>`;
881
+ });
882
+
883
+ mediumMatches.forEach(risk => {
884
+ html += `<div class="risk-item medium"><span class="risk-icon">🟡</span><span>${risk.replace(/🟡\s*/, '')}</span></div>`;
885
+ });
886
+
887
+ lowMatches.forEach(risk => {
888
+ html += `<div class="risk-item low"><span class="risk-icon">🟢</span><span>${risk.replace(/🟢\s*/, '')}</span></div>`;
889
+ });
890
+
891
+ html += `</div>`;
892
+ return html;
893
+
640
894
  }