@lakindu_perera/toren 1.0.7 → 1.0.9

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 (61) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +120 -31
  3. package/dist/cli/toren.d.ts +24 -0
  4. package/dist/cli/toren.js +254 -0
  5. package/dist/cli/toren.js.map +1 -0
  6. package/dist/detectors/config-detector.d.ts +3 -0
  7. package/dist/detectors/config-detector.js +58 -0
  8. package/dist/detectors/config-detector.js.map +1 -0
  9. package/dist/detectors/health-detector.d.ts +12 -0
  10. package/dist/detectors/health-detector.js +53 -0
  11. package/dist/detectors/health-detector.js.map +1 -0
  12. package/dist/detectors/important-files-detector.d.ts +51 -0
  13. package/dist/detectors/important-files-detector.js +455 -0
  14. package/dist/detectors/important-files-detector.js.map +1 -0
  15. package/dist/detectors/package-manager-detector.d.ts +7 -0
  16. package/dist/detectors/package-manager-detector.js +35 -0
  17. package/dist/detectors/package-manager-detector.js.map +1 -0
  18. package/dist/detectors/project-info-detector.d.ts +13 -0
  19. package/dist/detectors/project-info-detector.js +97 -0
  20. package/dist/detectors/project-info-detector.js.map +1 -0
  21. package/dist/detectors/script-detector.d.ts +42 -0
  22. package/dist/detectors/script-detector.js +199 -0
  23. package/dist/detectors/script-detector.js.map +1 -0
  24. package/dist/focused-output.d.ts +27 -0
  25. package/dist/focused-output.js +201 -0
  26. package/dist/focused-output.js.map +1 -0
  27. package/dist/lifecycle.d.ts +2 -0
  28. package/dist/lifecycle.js +114 -0
  29. package/dist/lifecycle.js.map +1 -0
  30. package/dist/renderers/console-renderer.d.ts +19 -0
  31. package/dist/renderers/console-renderer.js +361 -0
  32. package/dist/renderers/console-renderer.js.map +1 -0
  33. package/dist/renderers/html-renderer.d.ts +14 -0
  34. package/{src → dist}/renderers/html-renderer.js +263 -201
  35. package/dist/renderers/html-renderer.js.map +1 -0
  36. package/dist/renderers/index.d.ts +6 -0
  37. package/dist/renderers/index.js +12 -0
  38. package/dist/renderers/index.js.map +1 -0
  39. package/dist/renderers/json-renderer.d.ts +10 -0
  40. package/dist/renderers/json-renderer.js +127 -0
  41. package/dist/renderers/json-renderer.js.map +1 -0
  42. package/dist/renderers/markdown-renderer.d.ts +13 -0
  43. package/dist/renderers/markdown-renderer.js +360 -0
  44. package/dist/renderers/markdown-renderer.js.map +1 -0
  45. package/dist/scanner/scan.d.ts +2 -0
  46. package/dist/scanner/scan.js +573 -0
  47. package/dist/scanner/scan.js.map +1 -0
  48. package/dist/types/index.d.ts +85 -0
  49. package/dist/types/index.js +2 -0
  50. package/dist/types/index.js.map +1 -0
  51. package/package.json +16 -7
  52. package/bin/toren.js +0 -308
  53. package/src/detectors/config-detector.js +0 -81
  54. package/src/detectors/script-detector.js +0 -32
  55. package/src/focused-output.js +0 -146
  56. package/src/lifecycle.js +0 -124
  57. package/src/renderers/console-renderer.js +0 -350
  58. package/src/renderers/index.js +0 -41
  59. package/src/renderers/json-renderer.js +0 -154
  60. package/src/renderers/markdown-renderer.js +0 -337
  61. package/src/scanner/scan.js +0 -532
@@ -24,14 +24,10 @@
24
24
  *
25
25
  * @module renderers/html-renderer
26
26
  */
27
-
28
27
  import path from 'node:path';
29
-
30
28
  // ---------------------------------------------------------------------------
31
29
  // Tree builder (plain-text, HTML-safe — same algorithm as markdown-renderer)
32
30
  // ---------------------------------------------------------------------------
33
-
34
-
35
31
  /**
36
32
  * Recursively serialise a tree node into classic tree-connector lines.
37
33
  *
@@ -43,35 +39,23 @@ import path from 'node:path';
43
39
  * @param {number} maxDepth
44
40
  */
45
41
  function serializeNode(node, prefix, isLast, lines, depth = 0, maxDepth = 5) {
46
- if (depth >= maxDepth) return;
47
-
48
- const connector = isLast ? '└── ' : '├── ';
49
- const childPad = isLast ? ' ' : '│ ';
50
- const label = node.type === 'directory' ? `${node.name}/` : node.name;
51
-
52
- lines.push(`${prefix}${connector}${label}`);
53
-
54
- if (node.type === 'directory') {
55
- const children = node.children || [];
56
-
57
- if (depth === maxDepth - 1 && children.length > 0) {
58
- lines.push(`${prefix}${childPad}└── ...`);
59
- return;
60
- }
61
-
62
- for (let i = 0; i < children.length; i++) {
63
- serializeNode(
64
- children[i],
65
- prefix + childPad,
66
- i === children.length - 1,
67
- lines,
68
- depth + 1,
69
- maxDepth,
70
- );
42
+ if (depth >= maxDepth)
43
+ return;
44
+ const connector = isLast ? '└── ' : '├── ';
45
+ const childPad = isLast ? ' ' : '│ ';
46
+ const label = node.type === 'directory' ? `${node.name}/` : node.name;
47
+ lines.push(`${prefix}${connector}${label}`);
48
+ if (node.type === 'directory') {
49
+ const children = node.children || [];
50
+ if (depth === maxDepth - 1 && children.length > 0) {
51
+ lines.push(`${prefix}${childPad}└── ...`);
52
+ return;
53
+ }
54
+ for (let i = 0; i < children.length; i++) {
55
+ serializeNode(children[i], prefix + childPad, i === children.length - 1, lines, depth + 1, maxDepth);
56
+ }
71
57
  }
72
- }
73
58
  }
74
-
75
59
  /**
76
60
  * Convert a ScanResult tree into a plain-text tree string.
77
61
  *
@@ -81,22 +65,18 @@ function serializeNode(node, prefix, isLast, lines, depth = 0, maxDepth = 5) {
81
65
  * @returns {string}
82
66
  */
83
67
  function buildTreeString(tree, rootName, totalFiles) {
84
- if (totalFiles === 0) return 'No files scanned.';
85
-
86
- const lines = [`${rootName}/`];
87
- const children = tree.children || [];
88
-
89
- for (let i = 0; i < children.length; i++) {
90
- serializeNode(children[i], '', i === children.length - 1, lines);
91
- }
92
-
93
- return lines.join('\n');
68
+ if (totalFiles === 0)
69
+ return 'No files scanned.';
70
+ const lines = [`${rootName}/`];
71
+ const children = tree.children || [];
72
+ for (let i = 0; i < children.length; i++) {
73
+ serializeNode(children[i], '', i === children.length - 1, lines);
74
+ }
75
+ return lines.join('\n');
94
76
  }
95
-
96
77
  // ---------------------------------------------------------------------------
97
78
  // Utility helpers
98
79
  // ---------------------------------------------------------------------------
99
-
100
80
  /**
101
81
  * Escape a value for safe insertion into HTML text nodes and attributes.
102
82
  *
@@ -104,14 +84,13 @@ function buildTreeString(tree, rootName, totalFiles) {
104
84
  * @returns {string}
105
85
  */
106
86
  function esc(value) {
107
- return String(value ?? '')
108
- .replace(/&/g, '&amp;')
109
- .replace(/</g, '&lt;')
110
- .replace(/>/g, '&gt;')
111
- .replace(/"/g, '&quot;')
112
- .replace(/'/g, '&#39;');
87
+ return String(value ?? '')
88
+ .replace(/&/g, '&amp;')
89
+ .replace(/</g, '&lt;')
90
+ .replace(/>/g, '&gt;')
91
+ .replace(/"/g, '&quot;')
92
+ .replace(/'/g, '&#39;');
113
93
  }
114
-
115
94
  /**
116
95
  * Format a scan duration in milliseconds to a human-readable string.
117
96
  * Returns plain text only — callers are responsible for HTML-escaping via esc().
@@ -120,15 +99,15 @@ function esc(value) {
120
99
  * @returns {string}
121
100
  */
122
101
  function formatDuration(ms) {
123
- if (ms < 1) return '< 1 ms';
124
- if (ms >= 1000) return `${(ms / 1000).toFixed(2)} s`;
125
- return `${Math.round(ms)} ms`;
102
+ if (ms < 1)
103
+ return '< 1 ms';
104
+ if (ms >= 1000)
105
+ return `${(ms / 1000).toFixed(2)} s`;
106
+ return `${Math.round(ms)} ms`;
126
107
  }
127
-
128
108
  // ---------------------------------------------------------------------------
129
109
  // CSS (inlined — zero external dependencies)
130
110
  // ---------------------------------------------------------------------------
131
-
132
111
  /**
133
112
  * Build the complete <style> block for the report.
134
113
  * All colours are driven by CSS custom properties so they are easy to theme.
@@ -136,7 +115,7 @@ function formatDuration(ms) {
136
115
  * @returns {string}
137
116
  */
138
117
  function buildStyles() {
139
- return `<style>
118
+ return `<style>
140
119
  /* ── Reset ──────────────────────────────────────────────────── */
141
120
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
142
121
 
@@ -577,28 +556,24 @@ body {
577
556
  }
578
557
  </style>`;
579
558
  }
580
-
581
559
  // ---------------------------------------------------------------------------
582
560
  // SVG icon helpers (zero external dependency)
583
561
  // ---------------------------------------------------------------------------
584
-
585
562
  /** @returns {string} */
586
563
  const icon = {
587
- code: () => `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>`,
588
- folder: () => `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>`,
589
- file: () => `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>`,
590
- clock: () => `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>`,
591
- door: () => `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2H3a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10"/><polyline points="17 8 22 12 17 16"/><line x1="22" y1="12" x2="11" y2="12"/></svg>`,
592
- tree: () => `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>`,
593
- bar: () => `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>`,
594
- info: () => `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>`,
595
- terminal: () => `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>`,
564
+ code: () => `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>`,
565
+ folder: () => `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>`,
566
+ file: () => `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>`,
567
+ clock: () => `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>`,
568
+ door: () => `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2H3a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10"/><polyline points="17 8 22 12 17 16"/><line x1="22" y1="12" x2="11" y2="12"/></svg>`,
569
+ tree: () => `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>`,
570
+ bar: () => `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>`,
571
+ info: () => `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>`,
572
+ terminal: () => `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>`,
596
573
  };
597
-
598
574
  // ---------------------------------------------------------------------------
599
575
  // Section builders (one function per report section)
600
576
  // ---------------------------------------------------------------------------
601
-
602
577
  /**
603
578
  * Derive a frameworks array from the projectType string.
604
579
  * Returns [] when no specific framework is detected.
@@ -608,10 +583,10 @@ const icon = {
608
583
  * @returns {string[]}
609
584
  */
610
585
  function deriveFrameworks(projectType) {
611
- if (!projectType || projectType === 'Unknown') return [];
612
- return [projectType];
586
+ if (!projectType || projectType === 'Unknown')
587
+ return [];
588
+ return [projectType];
613
589
  }
614
-
615
590
  /**
616
591
  * Render the gradient page header.
617
592
  *
@@ -620,7 +595,7 @@ function deriveFrameworks(projectType) {
620
595
  * @returns {string}
621
596
  */
622
597
  function renderHeader(projectType, relRoot) {
623
- return `
598
+ return `
624
599
  <header class="header">
625
600
  <div class="header-top">
626
601
  <div>
@@ -643,7 +618,6 @@ function renderHeader(projectType, relRoot) {
643
618
  </div>
644
619
  </header>`;
645
620
  }
646
-
647
621
  /**
648
622
  * Render the four summary stat cards.
649
623
  *
@@ -651,38 +625,36 @@ function renderHeader(projectType, relRoot) {
651
625
  * @returns {string}
652
626
  */
653
627
  function renderSummaryCards(result) {
654
- const { projectType, flatFiles, totalFolders, scanDurationMs } = result;
655
-
656
- const cards = [
657
- {
658
- label: 'Project Type',
659
- icon: icon.code(),
660
- value: esc(projectType || 'Unknown'),
661
- isText: true,
662
- sub: 'detected framework',
663
- },
664
- {
665
- label: 'Total Files',
666
- icon: icon.file(),
667
- value: flatFiles.length,
668
- sub: 'source files',
669
- },
670
- {
671
- label: 'Total Folders',
672
- icon: icon.folder(),
673
- value: totalFolders,
674
- sub: 'directories',
675
- },
676
- {
677
- label: 'Scan Duration',
678
- icon: icon.clock(),
679
- value: esc(formatDuration(scanDurationMs)),
680
- isText: true,
681
- sub: 'wall-clock time',
682
- },
683
- ];
684
-
685
- const cardHTML = cards.map(c => `
628
+ const { projectType, flatFiles, totalFolders, scanDurationMs } = result;
629
+ const cards = [
630
+ {
631
+ label: 'Project Type',
632
+ icon: icon.code(),
633
+ value: esc(projectType || 'Unknown'),
634
+ isText: true,
635
+ sub: 'detected framework',
636
+ },
637
+ {
638
+ label: 'Total Files',
639
+ icon: icon.file(),
640
+ value: flatFiles.length,
641
+ sub: 'source files',
642
+ },
643
+ {
644
+ label: 'Total Folders',
645
+ icon: icon.folder(),
646
+ value: totalFolders,
647
+ sub: 'directories',
648
+ },
649
+ {
650
+ label: 'Scan Duration',
651
+ icon: icon.clock(),
652
+ value: esc(formatDuration(scanDurationMs)),
653
+ isText: true,
654
+ sub: 'wall-clock time',
655
+ },
656
+ ];
657
+ const cardHTML = cards.map(c => `
686
658
  <div class="card">
687
659
  <div class="card-label">
688
660
  <span class="card-icon">${c.icon}</span>
@@ -691,10 +663,8 @@ function renderSummaryCards(result) {
691
663
  <div class="card-value${c.isText ? ' is-text' : ''}">${c.value}</div>
692
664
  <div class="card-sub">${esc(c.sub)}</div>
693
665
  </div>`).join('');
694
-
695
- return `<div class="cards">${cardHTML}</div>`;
666
+ return `<div class="cards">${cardHTML}</div>`;
696
667
  }
697
-
698
668
  /**
699
669
  * Render the frameworks section.
700
670
  *
@@ -702,24 +672,21 @@ function renderSummaryCards(result) {
702
672
  * @returns {string}
703
673
  */
704
674
  function renderFrameworks(projectType) {
705
- const frameworks = deriveFrameworks(projectType);
706
- const count = frameworks.length;
707
-
708
- const body = count === 0
709
- ? `<p class="empty-msg">No frameworks detected.</p>`
710
- : `<ul class="entry-list">
675
+ const frameworks = deriveFrameworks(projectType);
676
+ const count = frameworks.length;
677
+ const body = count === 0
678
+ ? `<p class="empty-msg">No frameworks detected.</p>`
679
+ : `<ul class="entry-list">
711
680
  ${frameworks.map(fw => `
712
681
  <li class="entry-item">
713
682
  <span class="entry-dot"></span>
714
683
  ${esc(fw)}
715
684
  </li>`).join('')}
716
685
  </ul>`;
717
-
718
- const countBadge = count > 0
719
- ? `<span class="section-count">${count} found</span>`
720
- : '';
721
-
722
- return `
686
+ const countBadge = count > 0
687
+ ? `<span class="section-count">${count} found</span>`
688
+ : '';
689
+ return `
723
690
  <section class="section">
724
691
  <div class="section-header">
725
692
  <div class="section-icon">${icon.code()}</div>
@@ -729,7 +696,6 @@ function renderFrameworks(projectType) {
729
696
  <div class="section-body">${body}</div>
730
697
  </section>`;
731
698
  }
732
-
733
699
  /**
734
700
  * Render the entry points section.
735
701
  *
@@ -737,23 +703,20 @@ function renderFrameworks(projectType) {
737
703
  * @returns {string}
738
704
  */
739
705
  function renderEntryPoints(entryPoints) {
740
- const count = entryPoints.length;
741
-
742
- const body = count === 0
743
- ? `<p class="empty-msg">No entry points detected.</p>`
744
- : `<ul class="entry-list">
706
+ const count = entryPoints.length;
707
+ const body = count === 0
708
+ ? `<p class="empty-msg">No entry points detected.</p>`
709
+ : `<ul class="entry-list">
745
710
  ${entryPoints.map(ep => `
746
711
  <li class="entry-item">
747
712
  <span class="entry-dot"></span>
748
713
  ${esc(ep)}
749
714
  </li>`).join('')}
750
715
  </ul>`;
751
-
752
- const countBadge = count > 0
753
- ? `<span class="section-count">${count} found</span>`
754
- : '';
755
-
756
- return `
716
+ const countBadge = count > 0
717
+ ? `<span class="section-count">${count} found</span>`
718
+ : '';
719
+ return `
757
720
  <section class="section">
758
721
  <div class="section-header">
759
722
  <div class="section-icon">${icon.door()}</div>
@@ -763,7 +726,6 @@ function renderEntryPoints(entryPoints) {
763
726
  <div class="section-body">${body}</div>
764
727
  </section>`;
765
728
  }
766
-
767
729
  /**
768
730
  * Render the configuration files section.
769
731
  *
@@ -771,23 +733,20 @@ function renderEntryPoints(entryPoints) {
771
733
  * @returns {string}
772
734
  */
773
735
  function renderConfigurationFiles(configs) {
774
- const count = configs.length;
775
-
776
- const body = count === 0
777
- ? `<p class="empty-msg">No configuration files detected.</p>`
778
- : `<ul class="entry-list">
736
+ const count = configs.length;
737
+ const body = count === 0
738
+ ? `<p class="empty-msg">No configuration files detected.</p>`
739
+ : `<ul class="entry-list">
779
740
  ${configs.map(c => `
780
741
  <li class="entry-item">
781
742
  <span class="entry-dot"></span>
782
743
  ${esc(c)}
783
744
  </li>`).join('')}
784
745
  </ul>`;
785
-
786
- const countBadge = count > 0
787
- ? `<span class="section-count">${count} found</span>`
788
- : '';
789
-
790
- return `
746
+ const countBadge = count > 0
747
+ ? `<span class="section-count">${count} found</span>`
748
+ : '';
749
+ return `
791
750
  <section class="section">
792
751
  <div class="section-header">
793
752
  <div class="section-icon">${icon.file()}</div>
@@ -797,49 +756,162 @@ function renderConfigurationFiles(configs) {
797
756
  <div class="section-body">${body}</div>
798
757
  </section>`;
799
758
  }
800
-
801
759
  /**
802
760
  * Render the package scripts section.
803
761
  *
804
762
  * @param {Array<{name: string, command: string}>} scripts
805
763
  * @returns {string}
806
764
  */
765
+ function renderProjectInfo(projectInfo, packageManager) {
766
+ const rows = [];
767
+ if (projectInfo?.name)
768
+ rows.push(['Name', projectInfo.name]);
769
+ if (packageManager)
770
+ rows.push(['Package Manager', packageManager]);
771
+ if (projectInfo?.runtime)
772
+ rows.push(['Runtime', projectInfo.runtime]);
773
+ if (projectInfo?.language)
774
+ rows.push(['Language', projectInfo.language]);
775
+ if (projectInfo?.architecture)
776
+ rows.push(['Architecture', projectInfo.architecture]);
777
+ if (projectInfo?.framework)
778
+ rows.push(['Framework', projectInfo.framework]);
779
+ if (projectInfo?.entryPoint)
780
+ rows.push(['Entry Point', projectInfo.entryPoint]);
781
+ if (projectInfo?.sourceDirectory)
782
+ rows.push(['Source Directory', projectInfo.sourceDirectory]);
783
+ if (rows.length === 0)
784
+ return '';
785
+ const rowsHTML = rows.map(([prop, value]) => `
786
+ <tr>
787
+ <td>${esc(prop)}</td>
788
+ <td class="val-plain">${esc(value)}</td>
789
+ </tr>`).join('');
790
+ return `
791
+ <section class="section">
792
+ <div class="section-header">
793
+ <div class="section-icon">${icon.info()}</div>
794
+ <h2 class="section-title">Project Info</h2>
795
+ </div>
796
+ <div class="section-body" style="padding:0">
797
+ <table class="data-table">
798
+ <thead>
799
+ <tr>
800
+ <th>Property</th>
801
+ <th style="text-align:right">Value</th>
802
+ </tr>
803
+ </thead>
804
+ <tbody>${rowsHTML}</tbody>
805
+ </table>
806
+ </div>
807
+ </section>`;
808
+ }
809
+ function renderImportantFiles(importantFiles) {
810
+ const count = importantFiles.length;
811
+ const body = count === 0
812
+ ? `<p class="empty-msg" style="padding: 1.5rem">No important files detected.</p>`
813
+ : `<table class="data-table">
814
+ <thead>
815
+ <tr>
816
+ <th>Path</th>
817
+ <th>Reason</th>
818
+ </tr>
819
+ </thead>
820
+ <tbody>
821
+ ${importantFiles.map(f => `
822
+ <tr>
823
+ <td><code>${esc(f.path)}</code></td>
824
+ <td class="val-plain">${esc(f.reason)}</td>
825
+ </tr>`).join('')}
826
+ </tbody>
827
+ </table>`;
828
+ const countBadge = count > 0
829
+ ? `<span class="section-count">${count} found</span>`
830
+ : '';
831
+ return `
832
+ <section class="section">
833
+ <div class="section-header">
834
+ <div class="section-icon">${icon.file()}</div>
835
+ <h2 class="section-title">Important Files</h2>
836
+ ${countBadge}
837
+ </div>
838
+ <div class="section-body" style="padding:0">
839
+ ${body}
840
+ </div>
841
+ </section>`;
842
+ }
843
+ function renderProjectHealth(health) {
844
+ const count = health.length;
845
+ const body = count === 0
846
+ ? `<p class="empty-msg" style="padding: 1.5rem">No project health observations available.</p>`
847
+ : `<ul class="entry-list">
848
+ ${health.map(h => {
849
+ let emoji = 'ℹ';
850
+ if (h.status === 'pass')
851
+ emoji = '✓';
852
+ else if (h.status === 'warning')
853
+ emoji = '⚠';
854
+ return `
855
+ <li class="entry-item">
856
+ <span>${esc(emoji)}</span>
857
+ ${esc(h.message)}
858
+ </li>`;
859
+ }).join('')}
860
+ </ul>`;
861
+ const countBadge = count > 0
862
+ ? `<span class="section-count">${count} observations</span>`
863
+ : '';
864
+ return `
865
+ <section class="section">
866
+ <div class="section-header">
867
+ <div class="section-icon">${icon.info()}</div>
868
+ <h2 class="section-title">Project Health</h2>
869
+ ${countBadge}
870
+ </div>
871
+ <div class="section-body">
872
+ ${body}
873
+ </div>
874
+ </section>`;
875
+ }
807
876
  function renderPackageScripts(scripts) {
808
- const count = scripts.length;
809
-
810
- const body = count === 0
811
- ? `<p class="empty-msg" style="padding: 1.5rem">No package scripts detected.</p>`
812
- : `<table class="data-table">
877
+ const count = scripts.length;
878
+ const body = count === 0
879
+ ? `<p class="empty-msg" style="padding: 1.5rem">No package scripts detected.</p>`
880
+ : `<table class="data-table">
813
881
  <thead>
814
882
  <tr>
815
- <th>Script</th>
883
+ <th>Usage</th>
884
+ <th>Description</th>
816
885
  <th>Command</th>
817
886
  </tr>
818
887
  </thead>
819
888
  <tbody>
820
- ${scripts.map(s => `
889
+ ${scripts.map(s => {
890
+ const usage = s.usage || `npm run ${s.name}`;
891
+ return `
821
892
  <tr>
822
- <td><code>${esc(s.name)}</code></td>
893
+ <td><code>${esc(usage)}</code></td>
894
+ <td class="val-plain">${esc(s.description || '')}</td>
823
895
  <td class="val-plain"><code>${esc(s.command)}</code></td>
824
- </tr>`).join('')}
896
+ </tr>`;
897
+ }).join('')}
825
898
  </tbody>
826
899
  </table>`;
827
-
828
- const countBadge = count > 0
829
- ? `<span class="section-count">${count} found</span>`
830
- : '';
831
-
832
- return `
900
+ const countBadge = count > 0
901
+ ? `<span class="section-count">${count} found</span>`
902
+ : '';
903
+ return `
833
904
  <section class="section">
834
905
  <div class="section-header">
835
906
  <div class="section-icon">${icon.terminal()}</div>
836
907
  <h2 class="section-title">Package Scripts</h2>
837
908
  ${countBadge}
838
909
  </div>
839
- <div class="section-body" style="padding:0">${body}</div>
910
+ <div class="section-body" style="padding:0">
911
+ ${body}
912
+ </div>
840
913
  </section>`;
841
914
  }
842
-
843
915
  /**
844
916
  * Render the folder structure section.
845
917
  *
@@ -849,9 +921,8 @@ function renderPackageScripts(scripts) {
849
921
  * @returns {string}
850
922
  */
851
923
  function renderFolderStructure(tree, flatFiles, rootName) {
852
- const treeStr = buildTreeString(tree, rootName, flatFiles.length);
853
-
854
- return `
924
+ const treeStr = buildTreeString(tree, rootName, flatFiles.length);
925
+ return `
855
926
  <section class="section">
856
927
  <div class="section-header">
857
928
  <div class="section-icon">${icon.tree()}</div>
@@ -865,7 +936,6 @@ function renderFolderStructure(tree, flatFiles, rootName) {
865
936
  </div>
866
937
  </section>`;
867
938
  }
868
-
869
939
  /**
870
940
  * Render the statistics table.
871
941
  *
@@ -873,21 +943,18 @@ function renderFolderStructure(tree, flatFiles, rootName) {
873
943
  * @returns {string}
874
944
  */
875
945
  function renderStats(result) {
876
- const { flatFiles, totalFolders, scanDurationMs } = result;
877
-
878
- const rows = [
879
- ['Files', flatFiles.length, true],
880
- ['Folders', totalFolders, true],
881
- ['Scan Duration', formatDuration(scanDurationMs), false],
882
- ];
883
-
884
- const rowsHTML = rows.map(([metric, value, isNum]) => `
946
+ const { flatFiles, totalFolders, scanDurationMs } = result;
947
+ const rows = [
948
+ ['Files', flatFiles.length, true],
949
+ ['Folders', totalFolders, true],
950
+ ['Scan Duration', formatDuration(scanDurationMs), false],
951
+ ];
952
+ const rowsHTML = rows.map(([metric, value, isNum]) => `
885
953
  <tr>
886
954
  <td>${esc(metric)}</td>
887
955
  <td class="${isNum ? 'val' : 'val-plain'}">${esc(value)}</td>
888
956
  </tr>`).join('');
889
-
890
- return `
957
+ return `
891
958
  <section class="section">
892
959
  <div class="section-header">
893
960
  <div class="section-icon">${icon.bar()}</div>
@@ -906,25 +973,22 @@ function renderStats(result) {
906
973
  </div>
907
974
  </section>`;
908
975
  }
909
-
910
976
  /**
911
977
  * Render the scan provenance / metadata section.
912
978
  *
913
979
  * @returns {string}
914
980
  */
915
981
  function renderScanInfo() {
916
- const rows = [
917
- ['Generated by', '<strong>Toren</strong> — Codebase Onboarding Intelligence'],
918
- ['Output format', 'HTML'],
919
- ];
920
-
921
- const rowsHTML = rows.map(([label, value]) => `
982
+ const rows = [
983
+ ['Generated by', '<strong>Toren</strong> — Codebase Onboarding Intelligence'],
984
+ ['Output format', 'HTML'],
985
+ ];
986
+ const rowsHTML = rows.map(([label, value]) => `
922
987
  <tr>
923
988
  <td>${esc(label)}</td>
924
989
  <td class="val-plain">${value}</td>
925
990
  </tr>`).join('');
926
-
927
- return `
991
+ return `
928
992
  <section class="section">
929
993
  <div class="section-header">
930
994
  <div class="section-icon">${icon.info()}</div>
@@ -937,24 +1001,21 @@ function renderScanInfo() {
937
1001
  </div>
938
1002
  </section>`;
939
1003
  }
940
-
941
1004
  /**
942
1005
  * Render the page footer.
943
1006
  *
944
1007
  * @returns {string}
945
1008
  */
946
1009
  function renderFooter() {
947
- return `
1010
+ return `
948
1011
  <footer class="footer">
949
1012
  <span class="footer-brand">${icon.code()} Toren</span>
950
1013
  &nbsp;—&nbsp; Codebase Onboarding Intelligence
951
1014
  </footer>`;
952
1015
  }
953
-
954
1016
  // ---------------------------------------------------------------------------
955
1017
  // Public API
956
1018
  // ---------------------------------------------------------------------------
957
-
958
1019
  /**
959
1020
  * Render a ScanResult as a self-contained HTML5 report to stdout.
960
1021
  *
@@ -966,14 +1027,12 @@ function renderFooter() {
966
1027
  * @param {{ cwd?: string }} [options]
967
1028
  */
968
1029
  export function render(result, options = {}) {
969
- const { rootPath, projectType, entryPoints, configs = [], scripts = [], flatFiles, tree } = result;
970
-
971
- const cwd = options.cwd ?? process.cwd();
972
- const relRoot = path.relative(cwd, rootPath) || '.';
973
- const rootName = path.basename(rootPath) || relRoot;
974
- const title = `Toren — ${esc(path.basename(rootPath) || relRoot)}`;
975
-
976
- const html = `<!DOCTYPE html>
1030
+ const { rootPath, projectType, entryPoints, configs = [], scripts = [], flatFiles, tree, importantFiles = [], health = [], projectInfo, packageManager } = result;
1031
+ const cwd = options.cwd ?? process.cwd();
1032
+ const relRoot = path.relative(cwd, rootPath) || '.';
1033
+ const rootName = path.basename(rootPath) || relRoot;
1034
+ const title = `Toren — ${esc(path.basename(rootPath) || relRoot)}`;
1035
+ const html = `<!DOCTYPE html>
977
1036
  <html lang="en">
978
1037
  <head>
979
1038
  <meta charset="UTF-8">
@@ -990,6 +1049,9 @@ export function render(result, options = {}) {
990
1049
 
991
1050
  <main>
992
1051
  ${renderSummaryCards(result)}
1052
+ ${renderProjectInfo(projectInfo, packageManager)}
1053
+ ${renderProjectHealth(health)}
1054
+ ${renderImportantFiles(importantFiles)}
993
1055
  ${renderFrameworks(projectType)}
994
1056
  ${renderEntryPoints(entryPoints)}
995
1057
  ${renderConfigurationFiles(configs)}
@@ -1004,6 +1066,6 @@ export function render(result, options = {}) {
1004
1066
  </div>
1005
1067
  </body>
1006
1068
  </html>`;
1007
-
1008
- console.log(html);
1069
+ console.log(html);
1009
1070
  }
1071
+ //# sourceMappingURL=html-renderer.js.map