@lakindu_perera/toren 1.0.4 → 1.0.5
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/README.md +24 -7
- package/bin/toren.js +2 -0
- package/package.json +2 -1
- package/src/detectors/config-detector.js +81 -0
- package/src/detectors/script-detector.js +32 -0
- package/src/focused-output.js +26 -1
- package/src/renderers/console-renderer.js +24 -0
- package/src/renderers/html-renderer.js +80 -1
- package/src/renderers/json-renderer.js +4 -0
- package/src/renderers/markdown-renderer.js +47 -1
- package/src/scanner/scan.js +14 -0
package/README.md
CHANGED
|
@@ -67,6 +67,8 @@ toren --project-type
|
|
|
67
67
|
toren --frameworks
|
|
68
68
|
toren --entry-points
|
|
69
69
|
toren --structure
|
|
70
|
+
toren --configs
|
|
71
|
+
toren --scripts
|
|
70
72
|
|
|
71
73
|
# Lifecycle & Help Commands
|
|
72
74
|
toren --help
|
|
@@ -84,6 +86,8 @@ toren --uninstall
|
|
|
84
86
|
| `--frameworks` | Show detected frameworks only. |
|
|
85
87
|
| `--entry-points` | Show detected entry points only. |
|
|
86
88
|
| `--structure` | Show repository structure only. |
|
|
89
|
+
| `--configs` | Show detected project configuration files. |
|
|
90
|
+
| `--scripts` | Show available package scripts only. |
|
|
87
91
|
| `--format <type>` | Output format: `console` (default), `json`, `markdown`, `html`. |
|
|
88
92
|
| `--include-hidden` | Include hidden files and dot-directories in the scan. |
|
|
89
93
|
| `--max-files <N>` | Override the default 50,000-file scan limit. |
|
|
@@ -93,7 +97,7 @@ toren --uninstall
|
|
|
93
97
|
| `--uninstall` | Safely remove Toren from the global npm environment. |
|
|
94
98
|
|
|
95
99
|
> **Note:** `--format md` is not a valid alias. Use `--format markdown` in full.
|
|
96
|
-
> **Note:** Focused output flags (`--project-type`, `--frameworks`, `--entry-points`, `--structure`) are mutually exclusive.
|
|
100
|
+
> **Note:** Focused output flags (`--project-type`, `--frameworks`, `--entry-points`, `--structure`, `--configs`, `--scripts`) are mutually exclusive.
|
|
97
101
|
|
|
98
102
|
---
|
|
99
103
|
|
|
@@ -113,6 +117,19 @@ Frameworks:
|
|
|
113
117
|
$ toren --entry-points
|
|
114
118
|
Entry Points:
|
|
115
119
|
- src/main.tsx
|
|
120
|
+
|
|
121
|
+
$ toren --configs
|
|
122
|
+
Configuration Files
|
|
123
|
+
───────────────────
|
|
124
|
+
|
|
125
|
+
✓ package.json
|
|
126
|
+
✓ vite.config.ts
|
|
127
|
+
|
|
128
|
+
$ toren --scripts
|
|
129
|
+
Available Scripts
|
|
130
|
+
|
|
131
|
+
start node bin/toren.js
|
|
132
|
+
test node --test
|
|
116
133
|
```
|
|
117
134
|
|
|
118
135
|
### Console Output Example
|
|
@@ -121,7 +138,7 @@ The default `console` format renders a beautiful summary directly in your termin
|
|
|
121
138
|
```text
|
|
122
139
|
Toren v1.0.4 — Codebase Onboarding Intelligence
|
|
123
140
|
|
|
124
|
-
|
|
141
|
+
Project Summary
|
|
125
142
|
────────────────────────────────────────────────────────────────────────────────
|
|
126
143
|
Path: ./my-react-app
|
|
127
144
|
Project type: React
|
|
@@ -129,11 +146,11 @@ The default `console` format renders a beautiful summary directly in your termin
|
|
|
129
146
|
Total folders: 6
|
|
130
147
|
Scan duration: 4 ms
|
|
131
148
|
|
|
132
|
-
|
|
149
|
+
Entry Points
|
|
133
150
|
────────────────────────────────────────────────────────────────────────────────
|
|
134
151
|
→ src/main.tsx
|
|
135
152
|
|
|
136
|
-
|
|
153
|
+
Folder Structure (first 20 files)
|
|
137
154
|
────────────────────────────────────────────────────────────────────────────────
|
|
138
155
|
my-react-app/
|
|
139
156
|
├── public/
|
|
@@ -151,7 +168,7 @@ my-react-app/
|
|
|
151
168
|
… and 21 more file(s) not shown
|
|
152
169
|
|
|
153
170
|
────────────────────────────────────────────────────────────────────────────────
|
|
154
|
-
|
|
171
|
+
Scan complete.
|
|
155
172
|
```
|
|
156
173
|
|
|
157
174
|
### JSON Output Example
|
|
@@ -236,12 +253,12 @@ Contributions to improve this codebase analyzer CLI are always welcome!
|
|
|
236
253
|
|
|
237
254
|
---
|
|
238
255
|
|
|
239
|
-
##
|
|
256
|
+
## License
|
|
240
257
|
|
|
241
258
|
Distributed under the MIT License. See `LICENSE` for more information.
|
|
242
259
|
|
|
243
260
|
---
|
|
244
261
|
|
|
245
|
-
##
|
|
262
|
+
## Author
|
|
246
263
|
|
|
247
264
|
Built with by **[Lakindu Perera](https://github.com/lakindudev)**.
|
package/bin/toren.js
CHANGED
|
@@ -52,6 +52,8 @@ function printHelp() {
|
|
|
52
52
|
--frameworks Show detected frameworks only
|
|
53
53
|
--entry-points Show detected entry points only
|
|
54
54
|
--structure Show repository structure only
|
|
55
|
+
--configs Show detected project configuration files
|
|
56
|
+
--scripts Show available package scripts only
|
|
55
57
|
--format <type> Output as console, json, markdown, or html
|
|
56
58
|
--include-hidden Include hidden files and folders
|
|
57
59
|
--max-files <n> Set scan file limit
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lakindu_perera/toren",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "A powerful codebase scanner CLI tool for project analysis, framework detection, and understanding repository structure instantly.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"start": "node bin/toren.js",
|
|
17
|
+
"test": "node --test",
|
|
17
18
|
"lint": "node --check bin/toren.js src/scanner/scan.js src/lifecycle.js src/renderers/index.js src/renderers/console-renderer.js src/renderers/json-renderer.js src/renderers/markdown-renderer.js src/renderers/html-renderer.js"
|
|
18
19
|
},
|
|
19
20
|
"keywords": [
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Configuration file detector
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Detects common configuration files in the root of the repository.
|
|
7
|
+
*
|
|
8
|
+
* @param {string[]} flatFiles - Array of relative file paths from the scanner
|
|
9
|
+
* @returns {{ configs: string[] }}
|
|
10
|
+
*/
|
|
11
|
+
export function detectConfigs(flatFiles) {
|
|
12
|
+
const configPatterns = [
|
|
13
|
+
// Package
|
|
14
|
+
/^package\.json$/,
|
|
15
|
+
/^package-lock\.json$/,
|
|
16
|
+
/^pnpm-lock\.yaml$/,
|
|
17
|
+
/^yarn\.lock$/,
|
|
18
|
+
/^bun\.lockb$/,
|
|
19
|
+
|
|
20
|
+
// TypeScript
|
|
21
|
+
/^tsconfig\.json$/,
|
|
22
|
+
/^jsconfig\.json$/,
|
|
23
|
+
|
|
24
|
+
// Frontend
|
|
25
|
+
/^vite\.config\.[a-zA-Z0-9]+$/,
|
|
26
|
+
/^next\.config\.[a-zA-Z0-9]+$/,
|
|
27
|
+
/^nuxt\.config\.[a-zA-Z0-9]+$/,
|
|
28
|
+
/^astro\.config\.[a-zA-Z0-9]+$/,
|
|
29
|
+
/^angular\.json$/,
|
|
30
|
+
|
|
31
|
+
// Build
|
|
32
|
+
/^webpack\.config\.[a-zA-Z0-9]+$/,
|
|
33
|
+
/^rollup\.config\.[a-zA-Z0-9]+$/,
|
|
34
|
+
/^turbo\.json$/,
|
|
35
|
+
/^nx\.json$/,
|
|
36
|
+
|
|
37
|
+
// Styling
|
|
38
|
+
/^tailwind\.config\.[a-zA-Z0-9]+$/,
|
|
39
|
+
/^postcss\.config\.[a-zA-Z0-9]+$/,
|
|
40
|
+
|
|
41
|
+
// Lint
|
|
42
|
+
/^eslint\.config\.[a-zA-Z0-9]+$/,
|
|
43
|
+
/^\.eslintrc.*$/,
|
|
44
|
+
/^prettier\.config\.[a-zA-Z0-9]+$/,
|
|
45
|
+
/^\.prettierrc.*$/,
|
|
46
|
+
|
|
47
|
+
// Backend
|
|
48
|
+
/^pom\.xml$/,
|
|
49
|
+
/^build\.gradle$/,
|
|
50
|
+
/^build\.gradle\.kts$/,
|
|
51
|
+
|
|
52
|
+
// Languages
|
|
53
|
+
/^requirements\.txt$/,
|
|
54
|
+
/^pyproject\.toml$/,
|
|
55
|
+
/^Cargo\.toml$/,
|
|
56
|
+
/^go\.mod$/,
|
|
57
|
+
|
|
58
|
+
// Deployment
|
|
59
|
+
/^Dockerfile$/,
|
|
60
|
+
/^docker-compose\.ya?ml$/
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const matchedSet = new Set();
|
|
64
|
+
|
|
65
|
+
for (const file of flatFiles) {
|
|
66
|
+
// Ensure we only match files at the root of the repository
|
|
67
|
+
if (file.includes('/')) continue;
|
|
68
|
+
|
|
69
|
+
for (const pattern of configPatterns) {
|
|
70
|
+
if (pattern.test(file)) {
|
|
71
|
+
matchedSet.add(file);
|
|
72
|
+
break; // Stop checking patterns once a match is found
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Convert Set to Array and sort alphabetically
|
|
78
|
+
const configs = Array.from(matchedSet).sort();
|
|
79
|
+
|
|
80
|
+
return { configs };
|
|
81
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Detects npm/package scripts from package.json in the project root.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} rootPath - The absolute path of the scanned root
|
|
8
|
+
* @returns {{ scripts: Array<{name: string, command: string}> }}
|
|
9
|
+
*/
|
|
10
|
+
export function detectScripts(rootPath) {
|
|
11
|
+
const scripts = [];
|
|
12
|
+
const pkgPath = path.join(rootPath, 'package.json');
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
if (fs.existsSync(pkgPath)) {
|
|
16
|
+
const content = fs.readFileSync(pkgPath, 'utf8');
|
|
17
|
+
const pkg = JSON.parse(content);
|
|
18
|
+
|
|
19
|
+
if (pkg.scripts && typeof pkg.scripts === 'object') {
|
|
20
|
+
for (const [name, command] of Object.entries(pkg.scripts)) {
|
|
21
|
+
if (typeof command === 'string') {
|
|
22
|
+
scripts.push({ name, command });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
// Return empty scripts on malformed or unreadable package.json
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { scripts };
|
|
32
|
+
}
|
package/src/focused-output.js
CHANGED
|
@@ -8,7 +8,9 @@ export const FOCUSED_FLAGS = [
|
|
|
8
8
|
'--project-type',
|
|
9
9
|
'--frameworks',
|
|
10
10
|
'--entry-points',
|
|
11
|
-
'--structure'
|
|
11
|
+
'--structure',
|
|
12
|
+
'--configs',
|
|
13
|
+
'--scripts'
|
|
12
14
|
];
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -67,5 +69,28 @@ export function renderFocusedMode(mode, result) {
|
|
|
67
69
|
case '--structure':
|
|
68
70
|
renderStructure(result);
|
|
69
71
|
break;
|
|
72
|
+
|
|
73
|
+
case '--configs':
|
|
74
|
+
console.log('Configuration Files');
|
|
75
|
+
console.log('───────────────────\n');
|
|
76
|
+
if (!result.configs || result.configs.length === 0) {
|
|
77
|
+
console.log('No configuration files detected.');
|
|
78
|
+
} else {
|
|
79
|
+
result.configs.forEach(c => console.log(`✓ ${c}`));
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
|
|
83
|
+
case '--scripts':
|
|
84
|
+
console.log('Available Scripts\n');
|
|
85
|
+
if (!result.scripts || result.scripts.length === 0) {
|
|
86
|
+
console.log('No scripts found');
|
|
87
|
+
} else {
|
|
88
|
+
const maxNameLen = Math.max(...result.scripts.map(s => s.name.length));
|
|
89
|
+
result.scripts.forEach(s => {
|
|
90
|
+
const paddedName = s.name.padEnd(maxNameLen + 6, ' ');
|
|
91
|
+
console.log(`${paddedName}${s.command}`);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
70
95
|
}
|
|
71
96
|
}
|
|
@@ -173,6 +173,8 @@ export function render(result, options = {}) {
|
|
|
173
173
|
rootPath,
|
|
174
174
|
projectType,
|
|
175
175
|
entryPoints,
|
|
176
|
+
configs = [],
|
|
177
|
+
scripts = [],
|
|
176
178
|
tree,
|
|
177
179
|
flatFiles,
|
|
178
180
|
totalFolders,
|
|
@@ -204,6 +206,28 @@ export function render(result, options = {}) {
|
|
|
204
206
|
}
|
|
205
207
|
}
|
|
206
208
|
|
|
209
|
+
// ── Configuration Files ───────────────────────────────────────────────────
|
|
210
|
+
section('⚙️', 'Configuration Files');
|
|
211
|
+
if (configs.length === 0) {
|
|
212
|
+
console.log(paint(' No configuration files detected', C.dim));
|
|
213
|
+
} else {
|
|
214
|
+
for (const c of configs) {
|
|
215
|
+
console.log(` ${paint(c, C.white)}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ── Package Scripts ───────────────────────────────────────────────────────
|
|
220
|
+
section('📜', 'Package Scripts');
|
|
221
|
+
if (scripts.length === 0) {
|
|
222
|
+
console.log(paint(' No package scripts detected', C.dim));
|
|
223
|
+
} else {
|
|
224
|
+
const maxNameLen = Math.max(...scripts.map(s => s.name.length));
|
|
225
|
+
for (const s of scripts) {
|
|
226
|
+
const paddedName = s.name.padEnd(maxNameLen, ' ');
|
|
227
|
+
console.log(` ${paint(paddedName, C.white)} ${paint(s.command, C.dim)}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
207
231
|
// ── Structure Preview ─────────────────────────────────────────────────────
|
|
208
232
|
section('📁', `Folder Structure ${paint(`(first ${PREVIEW_LIMIT} files)`, C.dim)}`);
|
|
209
233
|
|
|
@@ -625,6 +625,7 @@ const icon = {
|
|
|
625
625
|
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>`,
|
|
626
626
|
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>`,
|
|
627
627
|
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>`,
|
|
628
|
+
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>`,
|
|
628
629
|
};
|
|
629
630
|
|
|
630
631
|
// ---------------------------------------------------------------------------
|
|
@@ -748,6 +749,82 @@ function renderEntryPoints(entryPoints) {
|
|
|
748
749
|
</div>`;
|
|
749
750
|
}
|
|
750
751
|
|
|
752
|
+
/**
|
|
753
|
+
* Render the configuration files section.
|
|
754
|
+
*
|
|
755
|
+
* @param {string[]} configs
|
|
756
|
+
* @returns {string}
|
|
757
|
+
*/
|
|
758
|
+
function renderConfigurationFiles(configs) {
|
|
759
|
+
const count = configs.length;
|
|
760
|
+
|
|
761
|
+
const body = count === 0
|
|
762
|
+
? `<p class="empty-msg">No configuration files detected.</p>`
|
|
763
|
+
: `<ul class="entry-list">
|
|
764
|
+
${configs.map(c => `
|
|
765
|
+
<li class="entry-item">
|
|
766
|
+
<span class="entry-dot"></span>
|
|
767
|
+
${esc(c)}
|
|
768
|
+
</li>`).join('')}
|
|
769
|
+
</ul>`;
|
|
770
|
+
|
|
771
|
+
const countBadge = count > 0
|
|
772
|
+
? `<span class="section-count">${count} found</span>`
|
|
773
|
+
: '';
|
|
774
|
+
|
|
775
|
+
return `
|
|
776
|
+
<div class="section">
|
|
777
|
+
<div class="section-header">
|
|
778
|
+
<div class="section-icon">${icon.file()}</div>
|
|
779
|
+
<span class="section-title">Configuration Files</span>
|
|
780
|
+
${countBadge}
|
|
781
|
+
</div>
|
|
782
|
+
<div class="section-body">${body}</div>
|
|
783
|
+
</div>`;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Render the package scripts section.
|
|
788
|
+
*
|
|
789
|
+
* @param {Array<{name: string, command: string}>} scripts
|
|
790
|
+
* @returns {string}
|
|
791
|
+
*/
|
|
792
|
+
function renderPackageScripts(scripts) {
|
|
793
|
+
const count = scripts.length;
|
|
794
|
+
|
|
795
|
+
const body = count === 0
|
|
796
|
+
? `<p class="empty-msg" style="padding: 1.5rem">No package scripts detected.</p>`
|
|
797
|
+
: `<table class="data-table">
|
|
798
|
+
<thead>
|
|
799
|
+
<tr>
|
|
800
|
+
<th>Script</th>
|
|
801
|
+
<th>Command</th>
|
|
802
|
+
</tr>
|
|
803
|
+
</thead>
|
|
804
|
+
<tbody>
|
|
805
|
+
${scripts.map(s => `
|
|
806
|
+
<tr>
|
|
807
|
+
<td><code>${esc(s.name)}</code></td>
|
|
808
|
+
<td class="val-plain"><code>${esc(s.command)}</code></td>
|
|
809
|
+
</tr>`).join('')}
|
|
810
|
+
</tbody>
|
|
811
|
+
</table>`;
|
|
812
|
+
|
|
813
|
+
const countBadge = count > 0
|
|
814
|
+
? `<span class="section-count">${count} found</span>`
|
|
815
|
+
: '';
|
|
816
|
+
|
|
817
|
+
return `
|
|
818
|
+
<div class="section">
|
|
819
|
+
<div class="section-header">
|
|
820
|
+
<div class="section-icon">${icon.terminal()}</div>
|
|
821
|
+
<span class="section-title">Package Scripts</span>
|
|
822
|
+
${countBadge}
|
|
823
|
+
</div>
|
|
824
|
+
<div class="section-body" style="padding:0">${body}</div>
|
|
825
|
+
</div>`;
|
|
826
|
+
}
|
|
827
|
+
|
|
751
828
|
/**
|
|
752
829
|
* Render the folder structure section with a dark <pre><code> tree.
|
|
753
830
|
*
|
|
@@ -873,7 +950,7 @@ function renderFooter() {
|
|
|
873
950
|
* @param {{ cwd?: string }} [options]
|
|
874
951
|
*/
|
|
875
952
|
export function render(result, options = {}) {
|
|
876
|
-
const { rootPath, projectType, entryPoints, flatFiles } = result;
|
|
953
|
+
const { rootPath, projectType, entryPoints, configs = [], scripts = [], flatFiles } = result;
|
|
877
954
|
|
|
878
955
|
const cwd = options.cwd ?? process.cwd();
|
|
879
956
|
const relRoot = path.relative(cwd, rootPath) || '.';
|
|
@@ -898,6 +975,8 @@ export function render(result, options = {}) {
|
|
|
898
975
|
<main>
|
|
899
976
|
${renderSummaryCards(result)}
|
|
900
977
|
${renderEntryPoints(entryPoints)}
|
|
978
|
+
${renderConfigurationFiles(configs)}
|
|
979
|
+
${renderPackageScripts(scripts)}
|
|
901
980
|
${renderFolderStructure(flatFiles, rootName)}
|
|
902
981
|
${renderStats(result)}
|
|
903
982
|
${renderScanInfo()}
|
|
@@ -26,6 +26,8 @@ export function render(result, options = {}) {
|
|
|
26
26
|
rootPath,
|
|
27
27
|
projectType,
|
|
28
28
|
entryPoints,
|
|
29
|
+
configs = [],
|
|
30
|
+
scripts = [],
|
|
29
31
|
tree,
|
|
30
32
|
flatFiles,
|
|
31
33
|
totalFolders,
|
|
@@ -47,6 +49,8 @@ export function render(result, options = {}) {
|
|
|
47
49
|
scanDurationMs: Math.round(scanDurationMs),
|
|
48
50
|
},
|
|
49
51
|
entryPoints,
|
|
52
|
+
configs,
|
|
53
|
+
scripts,
|
|
50
54
|
structure: (tree.children || []).map(mapTree)
|
|
51
55
|
};
|
|
52
56
|
|
|
@@ -218,6 +218,50 @@ function sectionSummary(out, result, relRoot) {
|
|
|
218
218
|
out.push('---');
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
/**
|
|
222
|
+
* @param {string[]} out
|
|
223
|
+
* @param {string[]} configs
|
|
224
|
+
*/
|
|
225
|
+
function sectionConfigurationFiles(out, configs) {
|
|
226
|
+
out.push('');
|
|
227
|
+
out.push('## Configuration Files');
|
|
228
|
+
out.push('');
|
|
229
|
+
|
|
230
|
+
if (configs.length === 0) {
|
|
231
|
+
out.push('No configuration files detected.');
|
|
232
|
+
} else {
|
|
233
|
+
for (const c of configs) {
|
|
234
|
+
out.push(`- ${c}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
out.push('');
|
|
239
|
+
out.push('---');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* @param {string[]} out
|
|
244
|
+
* @param {Array<{name: string, command: string}>} scripts
|
|
245
|
+
*/
|
|
246
|
+
function sectionPackageScripts(out, scripts) {
|
|
247
|
+
out.push('');
|
|
248
|
+
out.push('## Package Scripts');
|
|
249
|
+
out.push('');
|
|
250
|
+
|
|
251
|
+
if (scripts.length === 0) {
|
|
252
|
+
out.push('No package scripts detected.');
|
|
253
|
+
} else {
|
|
254
|
+
out.push('| Script | Command |');
|
|
255
|
+
out.push('|--------|---------|');
|
|
256
|
+
for (const s of scripts) {
|
|
257
|
+
out.push(`| \`${s.name}\` | \`${s.command}\` |`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
out.push('');
|
|
262
|
+
out.push('---');
|
|
263
|
+
}
|
|
264
|
+
|
|
221
265
|
/**
|
|
222
266
|
* @param {string[]} out
|
|
223
267
|
* @param {string[]} entryPoints
|
|
@@ -308,7 +352,7 @@ function sectionScanInfo(out) {
|
|
|
308
352
|
* @param {{ cwd?: string }} [options]
|
|
309
353
|
*/
|
|
310
354
|
export function render(result, options = {}) {
|
|
311
|
-
const { rootPath, entryPoints, flatFiles } = result;
|
|
355
|
+
const { rootPath, entryPoints, configs = [], scripts = [], flatFiles } = result;
|
|
312
356
|
|
|
313
357
|
const cwd = options.cwd ?? process.cwd();
|
|
314
358
|
const relRoot = path.relative(cwd, rootPath) || '.';
|
|
@@ -320,6 +364,8 @@ export function render(result, options = {}) {
|
|
|
320
364
|
sectionTitle(out);
|
|
321
365
|
sectionSummary(out, result, relRoot);
|
|
322
366
|
sectionEntryPoints(out, entryPoints);
|
|
367
|
+
sectionConfigurationFiles(out, configs);
|
|
368
|
+
sectionPackageScripts(out, scripts);
|
|
323
369
|
sectionFolderStructure(out, flatFiles, rootName);
|
|
324
370
|
sectionStatistics(out, result);
|
|
325
371
|
sectionScanInfo(out);
|
package/src/scanner/scan.js
CHANGED
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
|
|
21
21
|
import fs from 'node:fs';
|
|
22
22
|
import path from 'node:path';
|
|
23
|
+
import { detectConfigs } from '../detectors/config-detector.js';
|
|
24
|
+
import { detectScripts } from '../detectors/script-detector.js';
|
|
23
25
|
|
|
24
26
|
// ---------------------------------------------------------------------------
|
|
25
27
|
// Constants
|
|
@@ -96,6 +98,8 @@ const PROJECT_TYPE_MARKERS = [
|
|
|
96
98
|
* @property {string} rootPath - Absolute path that was scanned
|
|
97
99
|
* @property {string} projectType - Detected project type label
|
|
98
100
|
* @property {Array<string>} entryPoints - Relative paths of detected entry points
|
|
101
|
+
* @property {Array<string>} configs - Relative paths of detected config files
|
|
102
|
+
* @property {Array<{name: string, command: string}>} scripts - Parsed package scripts
|
|
99
103
|
* @property {DirNode} tree - Full in-memory file tree
|
|
100
104
|
* @property {Array<string>} flatFiles - All relative file paths (flat list)
|
|
101
105
|
* @property {number} totalFolders - Total number of directories walked
|
|
@@ -453,6 +457,10 @@ export function scan(targetPath, options = {}) {
|
|
|
453
457
|
const flatFiles = [];
|
|
454
458
|
/** @type {Array<string>} */
|
|
455
459
|
let entryPoints = [];
|
|
460
|
+
/** @type {Array<string>} */
|
|
461
|
+
let configs = [];
|
|
462
|
+
/** @type {Array<{name: string, command: string}>} */
|
|
463
|
+
let scripts = [];
|
|
456
464
|
|
|
457
465
|
const startTime = performance.now();
|
|
458
466
|
let tree;
|
|
@@ -465,6 +473,8 @@ export function scan(targetPath, options = {}) {
|
|
|
465
473
|
// Count all directory nodes in the tree (excluding root itself).
|
|
466
474
|
totalFolders = countFolders(tree) - 1;
|
|
467
475
|
entryPoints = findEntryPoints(projectType, flatFiles, rootPath);
|
|
476
|
+
configs = detectConfigs(flatFiles).configs;
|
|
477
|
+
scripts = detectScripts(rootPath).scripts;
|
|
468
478
|
} else if (stat.isFile()) {
|
|
469
479
|
const relFilePath = toPosix(path.basename(rootPath));
|
|
470
480
|
tree = {
|
|
@@ -481,6 +491,8 @@ export function scan(targetPath, options = {}) {
|
|
|
481
491
|
};
|
|
482
492
|
flatFiles.push(relFilePath);
|
|
483
493
|
entryPoints = [relFilePath]; // A single file is its own entry point
|
|
494
|
+
configs = detectConfigs(flatFiles).configs;
|
|
495
|
+
scripts = detectScripts(path.dirname(rootPath)).scripts;
|
|
484
496
|
} else {
|
|
485
497
|
throw new Error(`Path is neither a file nor a directory: ${rootPath}`);
|
|
486
498
|
}
|
|
@@ -491,6 +503,8 @@ export function scan(targetPath, options = {}) {
|
|
|
491
503
|
rootPath,
|
|
492
504
|
projectType,
|
|
493
505
|
entryPoints,
|
|
506
|
+
configs,
|
|
507
|
+
scripts,
|
|
494
508
|
tree,
|
|
495
509
|
flatFiles,
|
|
496
510
|
totalFolders,
|