@lakindu_perera/toren 1.0.4 → 1.0.7
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 +81 -44
- package/bin/toren.js +65 -44
- 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 +99 -24
- package/src/renderers/console-renderer.js +136 -44
- package/src/renderers/html-renderer.js +158 -62
- package/src/renderers/json-renderer.js +119 -19
- package/src/renderers/markdown-renderer.js +96 -87
- package/src/scanner/scan.js +37 -4
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
|
|
@@ -75,66 +77,54 @@ toren --doctor
|
|
|
75
77
|
toren --uninstall
|
|
76
78
|
```
|
|
77
79
|
|
|
78
|
-
###
|
|
80
|
+
### CLI Reference
|
|
79
81
|
|
|
80
|
-
| Flag | Description |
|
|
81
|
-
|
|
82
|
+
| Command / Flag | Description |
|
|
83
|
+
|----------------|-------------|
|
|
82
84
|
| `[path]` | Directory to scan. Defaults to the current directory (`.`). |
|
|
83
85
|
| `--project-type` | Show detected project type only. |
|
|
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
|
-
| `--max-files <
|
|
90
|
-
| `--help
|
|
91
|
-
| `--version
|
|
93
|
+
| `--max-files <n>` | Override the default 50,000-file scan limit. |
|
|
94
|
+
| `--help`, `-h` | Show usage and help message. |
|
|
95
|
+
| `--version`, `-v` | Print the installed version number. |
|
|
92
96
|
| `--doctor` | Diagnose the global installation health. |
|
|
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
|
|
|
100
|
-
## Output Examples
|
|
104
|
+
## Screenshots & Output Examples
|
|
101
105
|
|
|
102
|
-
###
|
|
103
|
-
|
|
106
|
+
### Console Output Example
|
|
107
|
+
The default `console` format renders a beautiful summary directly in your terminal:
|
|
104
108
|
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Project Type: Node.js / JavaScript
|
|
109
|
+
```text
|
|
110
|
+
Toren v1.0.6 — Codebase Onboarding Intelligence
|
|
108
111
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
- React
|
|
112
|
+
Project Summary
|
|
113
|
+
───────────────
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
Path: ./my-react-app
|
|
116
|
+
Project type: React
|
|
117
|
+
Total files: 32
|
|
118
|
+
Total folders: 6
|
|
117
119
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
+
Entry Points
|
|
121
|
+
────────────
|
|
122
|
+
|
|
123
|
+
src/main.tsx
|
|
124
|
+
|
|
125
|
+
Folder Structure (first 20 files)
|
|
126
|
+
──────────────────────────────────
|
|
120
127
|
|
|
121
|
-
```text
|
|
122
|
-
Toren v1.0.4 — Codebase Onboarding Intelligence
|
|
123
|
-
|
|
124
|
-
🔍 Project Summary
|
|
125
|
-
────────────────────────────────────────────────────────────────────────────────
|
|
126
|
-
Path: ./my-react-app
|
|
127
|
-
Project type: React
|
|
128
|
-
Total files: 32
|
|
129
|
-
Total folders: 6
|
|
130
|
-
Scan duration: 4 ms
|
|
131
|
-
|
|
132
|
-
🚪 Entry Points
|
|
133
|
-
────────────────────────────────────────────────────────────────────────────────
|
|
134
|
-
→ src/main.tsx
|
|
135
|
-
|
|
136
|
-
📁 Folder Structure (first 20 files)
|
|
137
|
-
────────────────────────────────────────────────────────────────────────────────
|
|
138
128
|
my-react-app/
|
|
139
129
|
├── public/
|
|
140
130
|
│ └── vite.svg
|
|
@@ -148,10 +138,46 @@ my-react-app/
|
|
|
148
138
|
├── index.html
|
|
149
139
|
├── package.json
|
|
150
140
|
└── vite.config.ts
|
|
151
|
-
|
|
141
|
+
… and 21 more file(s) not shown
|
|
152
142
|
|
|
153
|
-
|
|
154
|
-
|
|
143
|
+
Scan completed in 4 ms
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Focused Output Examples
|
|
147
|
+
Sometimes you only need a specific piece of intelligence for use in a script or a quick lookup. Use the focused output flags to bypass the full report:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
$ toren --project-type
|
|
151
|
+
Project Type
|
|
152
|
+
────────────
|
|
153
|
+
|
|
154
|
+
Node.js / JavaScript
|
|
155
|
+
|
|
156
|
+
$ toren --frameworks
|
|
157
|
+
Frameworks
|
|
158
|
+
──────────
|
|
159
|
+
|
|
160
|
+
React
|
|
161
|
+
|
|
162
|
+
$ toren --entry-points
|
|
163
|
+
Entry Points
|
|
164
|
+
────────────
|
|
165
|
+
|
|
166
|
+
src/main.tsx
|
|
167
|
+
|
|
168
|
+
$ toren --configs
|
|
169
|
+
Configuration Files
|
|
170
|
+
───────────────────
|
|
171
|
+
|
|
172
|
+
package.json
|
|
173
|
+
vite.config.ts
|
|
174
|
+
|
|
175
|
+
$ toren --scripts
|
|
176
|
+
Package Scripts
|
|
177
|
+
───────────────
|
|
178
|
+
|
|
179
|
+
start vite
|
|
180
|
+
test vitest
|
|
155
181
|
```
|
|
156
182
|
|
|
157
183
|
### JSON Output Example
|
|
@@ -190,6 +216,17 @@ Generate machine-readable output for scripts, toolchains, or AI context windows
|
|
|
190
216
|
|
|
191
217
|
---
|
|
192
218
|
|
|
219
|
+
## Release Notes
|
|
220
|
+
|
|
221
|
+
### v1.0.6 - CLI Experience Update
|
|
222
|
+
- **Redesigned CLI Output**: Completely revamped the console layout to use minimal typography, bold section titles, and dynamic divider lines matching the title width.
|
|
223
|
+
- **Improved Error Handling**: Transformed raw exceptions into user-friendly error messages outlining the issue and potential fixes, ensuring consistent exit codes.
|
|
224
|
+
- **Enhanced Focused Modes**: Focused output flags (`--configs`, `--entry-points`, `--structure`, etc.) now strictly mirror the design tokens of the full console renderer, providing identical spacing and layouts.
|
|
225
|
+
- **Streamlined Help & Version**: The `--help` interface was fully redesigned for faster reading, and `-v` / `--version` flags were added to output the exact binary version directly from `package.json`.
|
|
226
|
+
- **Accurate Timing Metrics**: Scan duration logic was improved to utilize native timing APIs, precisely measuring execution and printing results natively as "Scan completed in X ms" without clogging the project summary.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
193
230
|
## Architecture
|
|
194
231
|
|
|
195
232
|
Toren's internal architecture emphasizes modular design, separation of concerns, and a strict **zero dependency** philosophy.
|
|
@@ -236,12 +273,12 @@ Contributions to improve this codebase analyzer CLI are always welcome!
|
|
|
236
273
|
|
|
237
274
|
---
|
|
238
275
|
|
|
239
|
-
##
|
|
276
|
+
## License
|
|
240
277
|
|
|
241
278
|
Distributed under the MIT License. See `LICENSE` for more information.
|
|
242
279
|
|
|
243
280
|
---
|
|
244
281
|
|
|
245
|
-
##
|
|
282
|
+
## Author
|
|
246
283
|
|
|
247
284
|
Built with by **[Lakindu Perera](https://github.com/lakindudev)**.
|
package/bin/toren.js
CHANGED
|
@@ -38,41 +38,56 @@ const DEFAULT_FORMAT = 'console';
|
|
|
38
38
|
const SUPPORTED_FORMATS = Object.keys(renderers);
|
|
39
39
|
|
|
40
40
|
// ---------------------------------------------------------------------------
|
|
41
|
-
//
|
|
41
|
+
// Helpers
|
|
42
42
|
// ---------------------------------------------------------------------------
|
|
43
43
|
|
|
44
|
+
function printError(title, message, detailLabel, detailValue) {
|
|
45
|
+
console.error(`\x1b[31m✖ ${title}\x1b[0m\n`);
|
|
46
|
+
console.error(`${message}\n`);
|
|
47
|
+
if (detailLabel && detailValue) {
|
|
48
|
+
console.error(`${detailLabel}:\n`);
|
|
49
|
+
console.error(`${detailValue}\n`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
function printHelp() {
|
|
45
|
-
const formatList = SUPPORTED_FORMATS.map(f =>
|
|
54
|
+
const formatList = SUPPORTED_FORMATS.map(f => {
|
|
55
|
+
const suffix = f === DEFAULT_FORMAT ? ' (default)' : '';
|
|
56
|
+
return ` --format ${f.padEnd(10)}${suffix}`;
|
|
57
|
+
}).join('\n');
|
|
58
|
+
|
|
46
59
|
console.log(`
|
|
47
|
-
\x1b[
|
|
60
|
+
\x1b[1mToren\x1b[0m v${pkg.version}
|
|
61
|
+
Fast Repository Discovery CLI
|
|
62
|
+
|
|
63
|
+
\x1b[1mUSAGE\x1b[0m
|
|
48
64
|
toren [path] [options]
|
|
49
65
|
|
|
50
|
-
\x1b[
|
|
66
|
+
\x1b[1mCOMMANDS\x1b[0m
|
|
51
67
|
--project-type Show detected project type only
|
|
52
68
|
--frameworks Show detected frameworks only
|
|
53
69
|
--entry-points Show detected entry points only
|
|
54
70
|
--structure Show repository structure only
|
|
55
|
-
--
|
|
71
|
+
--configs Show detected project configuration files
|
|
72
|
+
--scripts Show available package scripts only
|
|
56
73
|
--include-hidden Include hidden files and folders
|
|
57
74
|
--max-files <n> Set scan file limit
|
|
58
|
-
--help Show this help message
|
|
59
|
-
--version Show version number
|
|
60
75
|
--doctor Run CLI diagnostics
|
|
61
76
|
--uninstall Remove Toren global installation
|
|
77
|
+
--help Show this help message
|
|
78
|
+
--version Show version number
|
|
62
79
|
|
|
63
|
-
\x1b[
|
|
80
|
+
\x1b[1mOUTPUT FORMATS\x1b[0m
|
|
64
81
|
${formatList}
|
|
65
82
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
\x1b[1mExamples:\x1b[0m
|
|
83
|
+
\x1b[1mEXAMPLES\x1b[0m
|
|
69
84
|
toren . Scan the current directory
|
|
70
85
|
toren ./my-project Scan a specific project folder
|
|
71
86
|
toren --format json . Output results as JSON
|
|
72
87
|
toren --format markdown . > out.md Save a Markdown report to a file
|
|
73
88
|
toren --format html . > out.html Save an HTML report to a file
|
|
74
|
-
toren --
|
|
75
|
-
toren
|
|
89
|
+
toren . --configs Show project configuration files
|
|
90
|
+
toren . --frameworks Show detected frameworks
|
|
76
91
|
`);
|
|
77
92
|
}
|
|
78
93
|
|
|
@@ -108,7 +123,7 @@ function parseArgs() {
|
|
|
108
123
|
}
|
|
109
124
|
|
|
110
125
|
if (args.includes('--version') || args.includes('-V') || args.includes('-v')) {
|
|
111
|
-
console.log(pkg.version);
|
|
126
|
+
console.log(`Toren ${pkg.version}`);
|
|
112
127
|
return { action: 'exit', code: 0 };
|
|
113
128
|
}
|
|
114
129
|
|
|
@@ -133,13 +148,12 @@ function parseArgs() {
|
|
|
133
148
|
const nextToken = args[formatIdx + 1];
|
|
134
149
|
// If the next token is missing or starts with '-', the user omitted the value.
|
|
135
150
|
if (nextToken === undefined || nextToken.startsWith('-')) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
console.error('');
|
|
151
|
+
printError(
|
|
152
|
+
'Missing value',
|
|
153
|
+
'The --format flag requires a valid output format.',
|
|
154
|
+
'Supported formats',
|
|
155
|
+
SUPPORTED_FORMATS.join(', ')
|
|
156
|
+
);
|
|
143
157
|
return { action: 'exit', code: 1 };
|
|
144
158
|
}
|
|
145
159
|
format = nextToken;
|
|
@@ -151,7 +165,7 @@ function parseArgs() {
|
|
|
151
165
|
const focusedInfo = getFocusedModeInfo(args);
|
|
152
166
|
|
|
153
167
|
if (focusedInfo.error) {
|
|
154
|
-
|
|
168
|
+
printError(focusedInfo.title, focusedInfo.message, focusedInfo.detailLabel, focusedInfo.detailValue);
|
|
155
169
|
return { action: 'exit', code: 1 };
|
|
156
170
|
}
|
|
157
171
|
|
|
@@ -174,17 +188,22 @@ function parseArgs() {
|
|
|
174
188
|
if (maxFilesIdx !== -1) {
|
|
175
189
|
const rawVal = args[maxFilesIdx + 1];
|
|
176
190
|
if (rawVal === undefined || rawVal.startsWith('-')) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
191
|
+
printError(
|
|
192
|
+
'Missing value',
|
|
193
|
+
'The --max-files flag requires a numeric value.',
|
|
194
|
+
'Example',
|
|
195
|
+
'toren --max-files 100000'
|
|
196
|
+
);
|
|
181
197
|
return { action: 'exit', code: 1 };
|
|
182
198
|
}
|
|
183
199
|
maxFiles = parseInt(rawVal, 10);
|
|
184
200
|
if (isNaN(maxFiles) || maxFiles < 1) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
201
|
+
printError(
|
|
202
|
+
'Invalid value',
|
|
203
|
+
'The --max-files flag must be a positive integer.',
|
|
204
|
+
'Provided value',
|
|
205
|
+
rawVal
|
|
206
|
+
);
|
|
188
207
|
return { action: 'exit', code: 1 };
|
|
189
208
|
}
|
|
190
209
|
consumedValues.add(rawVal);
|
|
@@ -211,8 +230,12 @@ function parseArgs() {
|
|
|
211
230
|
);
|
|
212
231
|
|
|
213
232
|
if (unknownFlag) {
|
|
214
|
-
|
|
215
|
-
|
|
233
|
+
printError(
|
|
234
|
+
'Unknown option',
|
|
235
|
+
'An unrecognized flag was provided.',
|
|
236
|
+
'Flag',
|
|
237
|
+
unknownFlag
|
|
238
|
+
);
|
|
216
239
|
return { action: 'exit', code: 1 };
|
|
217
240
|
}
|
|
218
241
|
|
|
@@ -232,16 +255,12 @@ function parseArgs() {
|
|
|
232
255
|
function assertValidFormat(format) {
|
|
233
256
|
if (renderers[format]) return;
|
|
234
257
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
console.error(list);
|
|
242
|
-
console.error('');
|
|
243
|
-
console.error(' Run \x1b[36mtoren --help\x1b[0m for usage.');
|
|
244
|
-
console.error('');
|
|
258
|
+
printError(
|
|
259
|
+
'Unsupported output format',
|
|
260
|
+
'The requested output format is not supported.',
|
|
261
|
+
'Format',
|
|
262
|
+
format
|
|
263
|
+
);
|
|
245
264
|
process.exit(1);
|
|
246
265
|
}
|
|
247
266
|
|
|
@@ -278,9 +297,11 @@ function assertValidFormat(format) {
|
|
|
278
297
|
if (parsed.format === 'json') {
|
|
279
298
|
console.error(JSON.stringify({ error: err.message }, null, 2));
|
|
280
299
|
} else {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
300
|
+
if (err.title) {
|
|
301
|
+
printError(err.title, err.message, err.detailLabel, err.detailValue);
|
|
302
|
+
} else {
|
|
303
|
+
printError('Scan failed', err.message);
|
|
304
|
+
}
|
|
284
305
|
}
|
|
285
306
|
process.exit(1);
|
|
286
307
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lakindu_perera/toren",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
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
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Toren — Focused Output Mode Handler
|
|
3
|
+
*
|
|
4
|
+
* Handles the six focused output flags:
|
|
5
|
+
* --project-type --frameworks --entry-points
|
|
6
|
+
* --structure --configs --scripts
|
|
7
|
+
*
|
|
8
|
+
* Design contract:
|
|
9
|
+
* - Each flag renders exactly one section to stdout using the console visual
|
|
10
|
+
* style (ANSI colours, section header + divider).
|
|
11
|
+
* - Empty-state messages are canonical strings declared in EMPTY below.
|
|
12
|
+
* They must end with a period and stay in sync with console-renderer.js.
|
|
13
|
+
* - No business logic. All data comes from the ScanResult passed in.
|
|
14
|
+
* - Stateless: renderFocusedMode() may be called multiple times safely.
|
|
3
15
|
*/
|
|
4
16
|
|
|
5
17
|
import { renderStructure } from './renderers/console-renderer.js';
|
|
6
18
|
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Canonical empty-state messages
|
|
21
|
+
// Keep these in sync with the equivalent strings in console-renderer.js.
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
const EMPTY = {
|
|
25
|
+
frameworks: 'No frameworks detected.',
|
|
26
|
+
entryPoints: 'No entry points detected.',
|
|
27
|
+
configs: 'No configuration files detected.',
|
|
28
|
+
scripts: 'No package scripts detected.',
|
|
29
|
+
};
|
|
30
|
+
|
|
7
31
|
export const FOCUSED_FLAGS = [
|
|
8
32
|
'--project-type',
|
|
9
33
|
'--frameworks',
|
|
10
34
|
'--entry-points',
|
|
11
|
-
'--structure'
|
|
35
|
+
'--structure',
|
|
36
|
+
'--configs',
|
|
37
|
+
'--scripts'
|
|
12
38
|
];
|
|
13
39
|
|
|
14
40
|
/**
|
|
@@ -23,8 +49,10 @@ export function getFocusedModeInfo(args) {
|
|
|
23
49
|
if (activeFlags.length > 1) {
|
|
24
50
|
return {
|
|
25
51
|
error: true,
|
|
26
|
-
|
|
27
|
-
|
|
52
|
+
title: 'Conflicting options',
|
|
53
|
+
message: 'Focused output flags are mutually exclusive.',
|
|
54
|
+
detailLabel: 'Provided flags',
|
|
55
|
+
detailValue: activeFlags.join(', ')
|
|
28
56
|
};
|
|
29
57
|
}
|
|
30
58
|
|
|
@@ -34,6 +62,37 @@ export function getFocusedModeInfo(args) {
|
|
|
34
62
|
};
|
|
35
63
|
}
|
|
36
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Print a bold-white section title followed by a dim matched-length divider
|
|
67
|
+
* and a blank line — same visual pattern as console-renderer.js's section().
|
|
68
|
+
*
|
|
69
|
+
* @param {string} title
|
|
70
|
+
*/
|
|
71
|
+
function printSectionHeader(title) {
|
|
72
|
+
console.log(`\x1b[1m\x1b[97m${title}\x1b[0m`);
|
|
73
|
+
console.log(`\x1b[2m${'─'.repeat(title.length)}\x1b[0m`);
|
|
74
|
+
console.log('');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Print a focused section: header, then items or an empty-state message.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} title - Section heading
|
|
81
|
+
* @param {string[]} items - Pre-formatted lines to print
|
|
82
|
+
* @param {string} emptyMsg - Canonical empty-state message (ends with '.')
|
|
83
|
+
*/
|
|
84
|
+
function section(title, items, emptyMsg) {
|
|
85
|
+
printSectionHeader(title);
|
|
86
|
+
|
|
87
|
+
if (!items || items.length === 0) {
|
|
88
|
+
console.log(`\x1b[2m${emptyMsg}\x1b[0m`);
|
|
89
|
+
} else {
|
|
90
|
+
for (const item of items) {
|
|
91
|
+
console.log(item);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
37
96
|
/**
|
|
38
97
|
* Renders the scan result based on the active focused mode.
|
|
39
98
|
*
|
|
@@ -42,30 +101,46 @@ export function getFocusedModeInfo(args) {
|
|
|
42
101
|
*/
|
|
43
102
|
export function renderFocusedMode(mode, result) {
|
|
44
103
|
switch (mode) {
|
|
45
|
-
case '--project-type':
|
|
46
|
-
|
|
104
|
+
case '--project-type': {
|
|
105
|
+
// projectType is always a string; 'Unknown' when undetected.
|
|
106
|
+
section('Project Type', [result.projectType], 'Unknown');
|
|
47
107
|
break;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
case '--frameworks': {
|
|
111
|
+
const frameworks = (!result.projectType || result.projectType === 'Unknown')
|
|
112
|
+
? []
|
|
113
|
+
: [result.projectType];
|
|
114
|
+
section('Frameworks', frameworks, EMPTY.frameworks);
|
|
56
115
|
break;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
} else {
|
|
62
|
-
console.log('Entry Points:');
|
|
63
|
-
result.entryPoints.forEach(ep => console.log(`- ${ep}`));
|
|
64
|
-
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
case '--entry-points': {
|
|
119
|
+
section('Entry Points', result.entryPoints || [], EMPTY.entryPoints);
|
|
65
120
|
break;
|
|
66
|
-
|
|
67
|
-
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
case '--structure': {
|
|
68
124
|
renderStructure(result);
|
|
69
125
|
break;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
case '--configs': {
|
|
129
|
+
section('Configuration Files', result.configs || [], EMPTY.configs);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
case '--scripts': {
|
|
134
|
+
const scriptsList = [];
|
|
135
|
+
if (result.scripts && result.scripts.length > 0) {
|
|
136
|
+
const maxNameLen = Math.max(...result.scripts.map(s => s.name.length));
|
|
137
|
+
for (const s of result.scripts) {
|
|
138
|
+
const paddedName = s.name.padEnd(maxNameLen, ' ');
|
|
139
|
+
scriptsList.push(`\x1b[97m${paddedName}\x1b[0m \x1b[2m${s.command}\x1b[0m`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
section('Package Scripts', scriptsList, EMPTY.scripts);
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
70
145
|
}
|
|
71
146
|
}
|