@lakindu_perera/toren 1.0.8 → 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.
- package/CHANGELOG.md +7 -0
- package/dist/cli/toren.d.ts +24 -0
- package/dist/cli/toren.js +254 -0
- package/dist/cli/toren.js.map +1 -0
- package/dist/detectors/config-detector.d.ts +3 -0
- package/dist/detectors/config-detector.js +58 -0
- package/dist/detectors/config-detector.js.map +1 -0
- package/dist/detectors/health-detector.d.ts +12 -0
- package/dist/detectors/health-detector.js +53 -0
- package/dist/detectors/health-detector.js.map +1 -0
- package/dist/detectors/important-files-detector.d.ts +51 -0
- package/dist/detectors/important-files-detector.js +455 -0
- package/dist/detectors/important-files-detector.js.map +1 -0
- package/dist/detectors/package-manager-detector.d.ts +7 -0
- package/dist/detectors/package-manager-detector.js +35 -0
- package/dist/detectors/package-manager-detector.js.map +1 -0
- package/dist/detectors/project-info-detector.d.ts +13 -0
- package/dist/detectors/project-info-detector.js +97 -0
- package/dist/detectors/project-info-detector.js.map +1 -0
- package/dist/detectors/script-detector.d.ts +42 -0
- package/dist/detectors/script-detector.js +199 -0
- package/dist/detectors/script-detector.js.map +1 -0
- package/dist/focused-output.d.ts +27 -0
- package/dist/focused-output.js +201 -0
- package/dist/focused-output.js.map +1 -0
- package/dist/lifecycle.d.ts +2 -0
- package/dist/lifecycle.js +114 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/renderers/console-renderer.d.ts +19 -0
- package/dist/renderers/console-renderer.js +361 -0
- package/dist/renderers/console-renderer.js.map +1 -0
- package/dist/renderers/html-renderer.d.ts +14 -0
- package/{src → dist}/renderers/html-renderer.js +181 -241
- package/dist/renderers/html-renderer.js.map +1 -0
- package/dist/renderers/index.d.ts +6 -0
- package/dist/renderers/index.js +12 -0
- package/dist/renderers/index.js.map +1 -0
- package/dist/renderers/json-renderer.d.ts +10 -0
- package/dist/renderers/json-renderer.js +127 -0
- package/dist/renderers/json-renderer.js.map +1 -0
- package/dist/renderers/markdown-renderer.d.ts +13 -0
- package/dist/renderers/markdown-renderer.js +360 -0
- package/dist/renderers/markdown-renderer.js.map +1 -0
- package/dist/scanner/scan.d.ts +2 -0
- package/dist/scanner/scan.js +573 -0
- package/dist/scanner/scan.js.map +1 -0
- package/dist/types/index.d.ts +85 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +11 -6
- package/bin/toren.js +0 -311
- package/src/detectors/config-detector.js +0 -81
- package/src/detectors/health-detector.js +0 -72
- package/src/detectors/important-files-detector.js +0 -515
- package/src/detectors/package-manager-detector.js +0 -112
- package/src/detectors/project-info-detector.js +0 -89
- package/src/detectors/script-detector.js +0 -248
- package/src/focused-output.js +0 -216
- package/src/lifecycle.js +0 -124
- package/src/renderers/console-renderer.js +0 -399
- package/src/renderers/index.js +0 -41
- package/src/renderers/json-renderer.js +0 -168
- package/src/renderers/markdown-renderer.js +0 -397
- package/src/scanner/scan.js +0 -554
|
@@ -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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
87
|
+
return String(value ?? '')
|
|
88
|
+
.replace(/&/g, '&')
|
|
89
|
+
.replace(/</g, '<')
|
|
90
|
+
.replace(/>/g, '>')
|
|
91
|
+
.replace(/"/g, '"')
|
|
92
|
+
.replace(/'/g, ''');
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
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
|
-
|
|
612
|
-
|
|
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
|
-
|
|
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
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
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
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
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
|
-
|
|
719
|
-
|
|
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
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
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
|
-
|
|
753
|
-
|
|
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
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
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
|
-
|
|
787
|
-
|
|
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,7 +756,6 @@ 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
|
*
|
|
@@ -805,25 +763,31 @@ function renderConfigurationFiles(configs) {
|
|
|
805
763
|
* @returns {string}
|
|
806
764
|
*/
|
|
807
765
|
function renderProjectInfo(projectInfo, packageManager) {
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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]) => `
|
|
821
786
|
<tr>
|
|
822
787
|
<td>${esc(prop)}</td>
|
|
823
788
|
<td class="val-plain">${esc(value)}</td>
|
|
824
789
|
</tr>`).join('');
|
|
825
|
-
|
|
826
|
-
return `
|
|
790
|
+
return `
|
|
827
791
|
<section class="section">
|
|
828
792
|
<div class="section-header">
|
|
829
793
|
<div class="section-icon">${icon.info()}</div>
|
|
@@ -842,13 +806,11 @@ function renderProjectInfo(projectInfo, packageManager) {
|
|
|
842
806
|
</div>
|
|
843
807
|
</section>`;
|
|
844
808
|
}
|
|
845
|
-
|
|
846
809
|
function renderImportantFiles(importantFiles) {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
: `<table class="data-table">
|
|
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">
|
|
852
814
|
<thead>
|
|
853
815
|
<tr>
|
|
854
816
|
<th>Path</th>
|
|
@@ -863,12 +825,10 @@ function renderImportantFiles(importantFiles) {
|
|
|
863
825
|
</tr>`).join('')}
|
|
864
826
|
</tbody>
|
|
865
827
|
</table>`;
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
return `
|
|
828
|
+
const countBadge = count > 0
|
|
829
|
+
? `<span class="section-count">${count} found</span>`
|
|
830
|
+
: '';
|
|
831
|
+
return `
|
|
872
832
|
<section class="section">
|
|
873
833
|
<div class="section-header">
|
|
874
834
|
<div class="section-icon">${icon.file()}</div>
|
|
@@ -880,30 +840,28 @@ function renderImportantFiles(importantFiles) {
|
|
|
880
840
|
</div>
|
|
881
841
|
</section>`;
|
|
882
842
|
}
|
|
883
|
-
|
|
884
843
|
function renderProjectHealth(health) {
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
: `<ul class="entry-list">
|
|
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">
|
|
890
848
|
${health.map(h => {
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
849
|
+
let emoji = 'ℹ';
|
|
850
|
+
if (h.status === 'pass')
|
|
851
|
+
emoji = '✓';
|
|
852
|
+
else if (h.status === 'warning')
|
|
853
|
+
emoji = '⚠';
|
|
854
|
+
return `
|
|
895
855
|
<li class="entry-item">
|
|
896
856
|
<span>${esc(emoji)}</span>
|
|
897
857
|
${esc(h.message)}
|
|
898
858
|
</li>`;
|
|
899
859
|
}).join('')}
|
|
900
860
|
</ul>`;
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
return `
|
|
861
|
+
const countBadge = count > 0
|
|
862
|
+
? `<span class="section-count">${count} observations</span>`
|
|
863
|
+
: '';
|
|
864
|
+
return `
|
|
907
865
|
<section class="section">
|
|
908
866
|
<div class="section-header">
|
|
909
867
|
<div class="section-icon">${icon.info()}</div>
|
|
@@ -915,13 +873,11 @@ function renderProjectHealth(health) {
|
|
|
915
873
|
</div>
|
|
916
874
|
</section>`;
|
|
917
875
|
}
|
|
918
|
-
|
|
919
876
|
function renderPackageScripts(scripts) {
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
: `<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">
|
|
925
881
|
<thead>
|
|
926
882
|
<tr>
|
|
927
883
|
<th>Usage</th>
|
|
@@ -938,15 +894,13 @@ function renderPackageScripts(scripts) {
|
|
|
938
894
|
<td class="val-plain">${esc(s.description || '')}</td>
|
|
939
895
|
<td class="val-plain"><code>${esc(s.command)}</code></td>
|
|
940
896
|
</tr>`;
|
|
941
|
-
|
|
897
|
+
}).join('')}
|
|
942
898
|
</tbody>
|
|
943
899
|
</table>`;
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
return `
|
|
900
|
+
const countBadge = count > 0
|
|
901
|
+
? `<span class="section-count">${count} found</span>`
|
|
902
|
+
: '';
|
|
903
|
+
return `
|
|
950
904
|
<section class="section">
|
|
951
905
|
<div class="section-header">
|
|
952
906
|
<div class="section-icon">${icon.terminal()}</div>
|
|
@@ -958,7 +912,6 @@ function renderPackageScripts(scripts) {
|
|
|
958
912
|
</div>
|
|
959
913
|
</section>`;
|
|
960
914
|
}
|
|
961
|
-
|
|
962
915
|
/**
|
|
963
916
|
* Render the folder structure section.
|
|
964
917
|
*
|
|
@@ -968,9 +921,8 @@ function renderPackageScripts(scripts) {
|
|
|
968
921
|
* @returns {string}
|
|
969
922
|
*/
|
|
970
923
|
function renderFolderStructure(tree, flatFiles, rootName) {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
return `
|
|
924
|
+
const treeStr = buildTreeString(tree, rootName, flatFiles.length);
|
|
925
|
+
return `
|
|
974
926
|
<section class="section">
|
|
975
927
|
<div class="section-header">
|
|
976
928
|
<div class="section-icon">${icon.tree()}</div>
|
|
@@ -984,7 +936,6 @@ function renderFolderStructure(tree, flatFiles, rootName) {
|
|
|
984
936
|
</div>
|
|
985
937
|
</section>`;
|
|
986
938
|
}
|
|
987
|
-
|
|
988
939
|
/**
|
|
989
940
|
* Render the statistics table.
|
|
990
941
|
*
|
|
@@ -992,21 +943,18 @@ function renderFolderStructure(tree, flatFiles, rootName) {
|
|
|
992
943
|
* @returns {string}
|
|
993
944
|
*/
|
|
994
945
|
function renderStats(result) {
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
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]) => `
|
|
1004
953
|
<tr>
|
|
1005
954
|
<td>${esc(metric)}</td>
|
|
1006
955
|
<td class="${isNum ? 'val' : 'val-plain'}">${esc(value)}</td>
|
|
1007
956
|
</tr>`).join('');
|
|
1008
|
-
|
|
1009
|
-
return `
|
|
957
|
+
return `
|
|
1010
958
|
<section class="section">
|
|
1011
959
|
<div class="section-header">
|
|
1012
960
|
<div class="section-icon">${icon.bar()}</div>
|
|
@@ -1025,25 +973,22 @@ function renderStats(result) {
|
|
|
1025
973
|
</div>
|
|
1026
974
|
</section>`;
|
|
1027
975
|
}
|
|
1028
|
-
|
|
1029
976
|
/**
|
|
1030
977
|
* Render the scan provenance / metadata section.
|
|
1031
978
|
*
|
|
1032
979
|
* @returns {string}
|
|
1033
980
|
*/
|
|
1034
981
|
function renderScanInfo() {
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
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]) => `
|
|
1041
987
|
<tr>
|
|
1042
988
|
<td>${esc(label)}</td>
|
|
1043
989
|
<td class="val-plain">${value}</td>
|
|
1044
990
|
</tr>`).join('');
|
|
1045
|
-
|
|
1046
|
-
return `
|
|
991
|
+
return `
|
|
1047
992
|
<section class="section">
|
|
1048
993
|
<div class="section-header">
|
|
1049
994
|
<div class="section-icon">${icon.info()}</div>
|
|
@@ -1056,24 +1001,21 @@ function renderScanInfo() {
|
|
|
1056
1001
|
</div>
|
|
1057
1002
|
</section>`;
|
|
1058
1003
|
}
|
|
1059
|
-
|
|
1060
1004
|
/**
|
|
1061
1005
|
* Render the page footer.
|
|
1062
1006
|
*
|
|
1063
1007
|
* @returns {string}
|
|
1064
1008
|
*/
|
|
1065
1009
|
function renderFooter() {
|
|
1066
|
-
|
|
1010
|
+
return `
|
|
1067
1011
|
<footer class="footer">
|
|
1068
1012
|
<span class="footer-brand">${icon.code()} Toren</span>
|
|
1069
1013
|
— Codebase Onboarding Intelligence
|
|
1070
1014
|
</footer>`;
|
|
1071
1015
|
}
|
|
1072
|
-
|
|
1073
1016
|
// ---------------------------------------------------------------------------
|
|
1074
1017
|
// Public API
|
|
1075
1018
|
// ---------------------------------------------------------------------------
|
|
1076
|
-
|
|
1077
1019
|
/**
|
|
1078
1020
|
* Render a ScanResult as a self-contained HTML5 report to stdout.
|
|
1079
1021
|
*
|
|
@@ -1085,14 +1027,12 @@ function renderFooter() {
|
|
|
1085
1027
|
* @param {{ cwd?: string }} [options]
|
|
1086
1028
|
*/
|
|
1087
1029
|
export function render(result, options = {}) {
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
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>
|
|
1096
1036
|
<html lang="en">
|
|
1097
1037
|
<head>
|
|
1098
1038
|
<meta charset="UTF-8">
|
|
@@ -1126,6 +1066,6 @@ export function render(result, options = {}) {
|
|
|
1126
1066
|
</div>
|
|
1127
1067
|
</body>
|
|
1128
1068
|
</html>`;
|
|
1129
|
-
|
|
1130
|
-
console.log(html);
|
|
1069
|
+
console.log(html);
|
|
1131
1070
|
}
|
|
1071
|
+
//# sourceMappingURL=html-renderer.js.map
|