@lakindu_perera/toren 1.0.5 → 1.0.8
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 +19 -0
- package/README.md +172 -63
- package/bin/toren.js +66 -44
- package/package.json +7 -3
- package/src/detectors/health-detector.js +72 -0
- package/src/detectors/important-files-detector.js +515 -0
- package/src/detectors/package-manager-detector.js +112 -0
- package/src/detectors/project-info-detector.js +89 -0
- package/src/detectors/script-detector.js +226 -10
- package/src/focused-output.js +161 -41
- package/src/renderers/console-renderer.js +170 -53
- package/src/renderers/html-renderer.js +212 -73
- package/src/renderers/json-renderer.js +133 -23
- package/src/renderers/markdown-renderer.js +127 -104
- package/src/scanner/scan.js +51 -10
|
@@ -28,37 +28,7 @@ import path from 'node:path';
|
|
|
28
28
|
// Plain-text tree builder (Markdown-safe, no ANSI, no emoji)
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
30
30
|
|
|
31
|
-
/**
|
|
32
|
-
* Build an in-memory nested tree from a flat list of relative file paths.
|
|
33
|
-
* This avoids repeating the walk already done by the scanner while keeping
|
|
34
|
-
* the markdown renderer completely self-contained.
|
|
35
|
-
*
|
|
36
|
-
* @param {string[]} flatFiles - Relative file paths produced by scan()
|
|
37
|
-
* @returns {{ name: string, type: 'directory'|'file', children: object }[]}
|
|
38
|
-
*/
|
|
39
|
-
function buildTree(flatFiles) {
|
|
40
|
-
/** @type {{ type: string, children: Record<string, object> }} */
|
|
41
|
-
const root = { type: 'directory', children: {} };
|
|
42
|
-
|
|
43
|
-
for (const filePath of flatFiles) {
|
|
44
|
-
const parts = filePath.split(/[/\\]/).filter(Boolean);
|
|
45
|
-
let node = root;
|
|
46
|
-
|
|
47
|
-
for (let i = 0; i < parts.length; i++) {
|
|
48
|
-
const part = parts[i];
|
|
49
|
-
const isLeaf = i === parts.length - 1;
|
|
50
|
-
|
|
51
|
-
if (!node.children[part]) {
|
|
52
|
-
node.children[part] = isLeaf
|
|
53
|
-
? { type: 'file', name: part }
|
|
54
|
-
: { type: 'directory', name: part, children: {} };
|
|
55
|
-
}
|
|
56
|
-
node = node.children[part];
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
31
|
|
|
60
|
-
return root;
|
|
61
|
-
}
|
|
62
32
|
|
|
63
33
|
/**
|
|
64
34
|
* Recursively serialise a tree node into classic tree-connector lines.
|
|
@@ -81,11 +51,7 @@ function serializeNode(node, prefix, isLast, lines, depth = 0, maxDepth = 5) {
|
|
|
81
51
|
lines.push(`${prefix}${connector}${label}`);
|
|
82
52
|
|
|
83
53
|
if (node.type === 'directory') {
|
|
84
|
-
const children =
|
|
85
|
-
// Directories before files, then alphabetically.
|
|
86
|
-
if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;
|
|
87
|
-
return a.name.localeCompare(b.name);
|
|
88
|
-
});
|
|
54
|
+
const children = node.children || [];
|
|
89
55
|
|
|
90
56
|
// Truncate deep directories with an ellipsis rather than cutting silently.
|
|
91
57
|
if (depth === maxDepth - 1 && children.length > 0) {
|
|
@@ -107,21 +73,18 @@ function serializeNode(node, prefix, isLast, lines, depth = 0, maxDepth = 5) {
|
|
|
107
73
|
}
|
|
108
74
|
|
|
109
75
|
/**
|
|
110
|
-
* Convert a
|
|
76
|
+
* Convert a ScanResult tree into a Markdown-safe tree string.
|
|
111
77
|
*
|
|
112
|
-
* @param {
|
|
78
|
+
* @param {import('../scanner/scan.js').DirNode} tree
|
|
113
79
|
* @param {string} rootName - Display name for the root node (e.g. "my-app/")
|
|
80
|
+
* @param {number} totalFiles
|
|
114
81
|
* @returns {string}
|
|
115
82
|
*/
|
|
116
|
-
function buildTreeString(
|
|
117
|
-
if (
|
|
83
|
+
function buildTreeString(tree, rootName, totalFiles) {
|
|
84
|
+
if (totalFiles === 0) return '';
|
|
118
85
|
|
|
119
|
-
const root = buildTree(flatFiles);
|
|
120
86
|
const lines = [`${rootName}/`];
|
|
121
|
-
const children =
|
|
122
|
-
if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;
|
|
123
|
-
return a.name.localeCompare(b.name);
|
|
124
|
-
});
|
|
87
|
+
const children = tree.children || [];
|
|
125
88
|
|
|
126
89
|
for (let i = 0; i < children.length; i++) {
|
|
127
90
|
serializeNode(children[i], '', i === children.length - 1, lines);
|
|
@@ -165,6 +128,19 @@ function markdownTable(rows) {
|
|
|
165
128
|
return [head, hr, ...body].join('\n');
|
|
166
129
|
}
|
|
167
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Derive a frameworks array from the projectType string.
|
|
133
|
+
* Returns [] when no specific framework is detected.
|
|
134
|
+
* Mirrors the identical derivation in console-renderer.js and json-renderer.js.
|
|
135
|
+
*
|
|
136
|
+
* @param {string} projectType
|
|
137
|
+
* @returns {string[]}
|
|
138
|
+
*/
|
|
139
|
+
function deriveFrameworks(projectType) {
|
|
140
|
+
if (!projectType || projectType === 'Unknown') return [];
|
|
141
|
+
return [projectType];
|
|
142
|
+
}
|
|
143
|
+
|
|
168
144
|
/**
|
|
169
145
|
* Build a right-aligned Markdown table (used for statistics).
|
|
170
146
|
*
|
|
@@ -190,11 +166,9 @@ function statsTable(rows) {
|
|
|
190
166
|
|
|
191
167
|
/** @param {string[]} out */
|
|
192
168
|
function sectionTitle(out) {
|
|
193
|
-
out.push('#
|
|
169
|
+
out.push('# Toren Report');
|
|
194
170
|
out.push('');
|
|
195
171
|
out.push('Generated by **Toren** — Codebase Onboarding Intelligence');
|
|
196
|
-
out.push('');
|
|
197
|
-
out.push('---');
|
|
198
172
|
}
|
|
199
173
|
|
|
200
174
|
/**
|
|
@@ -203,19 +177,48 @@ function sectionTitle(out) {
|
|
|
203
177
|
* @param {string} relRoot
|
|
204
178
|
*/
|
|
205
179
|
function sectionSummary(out, result, relRoot) {
|
|
206
|
-
const { projectType, flatFiles, totalFolders } = result;
|
|
180
|
+
const { projectType, flatFiles, totalFolders, packageManager, projectInfo } = result;
|
|
207
181
|
|
|
208
182
|
out.push('');
|
|
209
|
-
out.push('## Project
|
|
183
|
+
out.push('## Project');
|
|
210
184
|
out.push('');
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
])
|
|
185
|
+
|
|
186
|
+
const rows = [];
|
|
187
|
+
if (projectInfo && projectInfo.name) rows.push(['Name', projectInfo.name]);
|
|
188
|
+
rows.push(['Project Type', projectType]);
|
|
189
|
+
if (packageManager) rows.push(['Package Manager', packageManager]);
|
|
190
|
+
if (projectInfo && projectInfo.runtime) rows.push(['Runtime', projectInfo.runtime]);
|
|
191
|
+
if (projectInfo && projectInfo.language) rows.push(['Language', projectInfo.language]);
|
|
192
|
+
if (projectInfo && projectInfo.architecture) rows.push(['Architecture', projectInfo.architecture]);
|
|
193
|
+
if (projectInfo && projectInfo.framework) rows.push(['Framework', projectInfo.framework]);
|
|
194
|
+
if (projectInfo && projectInfo.entryPoint) rows.push(['Entry Point', projectInfo.entryPoint]);
|
|
195
|
+
if (projectInfo && projectInfo.sourceDirectory) rows.push(['Source Directory', projectInfo.sourceDirectory]);
|
|
196
|
+
|
|
197
|
+
rows.push(['Scan Path', relRoot]);
|
|
198
|
+
rows.push(['Total Files', String(flatFiles.length)]);
|
|
199
|
+
rows.push(['Total Folders', String(totalFolders)]);
|
|
200
|
+
|
|
201
|
+
out.push(markdownTable(rows));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @param {string[]} out
|
|
206
|
+
* @param {string} projectType
|
|
207
|
+
*/
|
|
208
|
+
function sectionFrameworks(out, projectType) {
|
|
209
|
+
const frameworks = deriveFrameworks(projectType);
|
|
210
|
+
|
|
211
|
+
out.push('');
|
|
212
|
+
out.push('## Frameworks');
|
|
217
213
|
out.push('');
|
|
218
|
-
|
|
214
|
+
|
|
215
|
+
if (frameworks.length === 0) {
|
|
216
|
+
out.push('No frameworks detected.');
|
|
217
|
+
} else {
|
|
218
|
+
for (const fw of frameworks) {
|
|
219
|
+
out.push(`- ${fw}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
219
222
|
}
|
|
220
223
|
|
|
221
224
|
/**
|
|
@@ -224,7 +227,7 @@ function sectionSummary(out, result, relRoot) {
|
|
|
224
227
|
*/
|
|
225
228
|
function sectionConfigurationFiles(out, configs) {
|
|
226
229
|
out.push('');
|
|
227
|
-
out.push('##
|
|
230
|
+
out.push('## Configurations');
|
|
228
231
|
out.push('');
|
|
229
232
|
|
|
230
233
|
if (configs.length === 0) {
|
|
@@ -234,32 +237,71 @@ function sectionConfigurationFiles(out, configs) {
|
|
|
234
237
|
out.push(`- ${c}`);
|
|
235
238
|
}
|
|
236
239
|
}
|
|
237
|
-
|
|
238
|
-
out.push('');
|
|
239
|
-
out.push('---');
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
/**
|
|
243
243
|
* @param {string[]} out
|
|
244
|
-
* @param {Array<{name: string, command: string}>} scripts
|
|
244
|
+
* @param {Array<{name: string, command: string, description: string, usage: string}>} scripts
|
|
245
245
|
*/
|
|
246
246
|
function sectionPackageScripts(out, scripts) {
|
|
247
247
|
out.push('');
|
|
248
|
-
out.push('##
|
|
248
|
+
out.push('## Scripts');
|
|
249
249
|
out.push('');
|
|
250
250
|
|
|
251
251
|
if (scripts.length === 0) {
|
|
252
252
|
out.push('No package scripts detected.');
|
|
253
253
|
} else {
|
|
254
|
-
out.push('| Script | Command |');
|
|
255
|
-
out.push('|--------|---------|');
|
|
256
254
|
for (const s of scripts) {
|
|
257
|
-
|
|
255
|
+
const usage = s.usage || `npm run ${s.name}`;
|
|
256
|
+
out.push(`- **${usage}**`);
|
|
257
|
+
if (s.description) {
|
|
258
|
+
out.push(` ${s.description} `);
|
|
259
|
+
out.push(` \`${s.command}\``);
|
|
260
|
+
} else {
|
|
261
|
+
out.push(` \`${s.command}\``);
|
|
262
|
+
}
|
|
258
263
|
}
|
|
259
264
|
}
|
|
265
|
+
}
|
|
260
266
|
|
|
267
|
+
/**
|
|
268
|
+
* @param {string[]} out
|
|
269
|
+
* @param {Array<{path: string, reason: string}>} importantFiles
|
|
270
|
+
*/
|
|
271
|
+
function sectionImportantFiles(out, importantFiles) {
|
|
272
|
+
out.push('');
|
|
273
|
+
out.push('## Important Files');
|
|
261
274
|
out.push('');
|
|
262
|
-
|
|
275
|
+
|
|
276
|
+
if (importantFiles.length === 0) {
|
|
277
|
+
out.push('No important files detected.');
|
|
278
|
+
} else {
|
|
279
|
+
for (const f of importantFiles) {
|
|
280
|
+
out.push(`- **${f.path}** `);
|
|
281
|
+
out.push(` ${f.reason}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* @param {string[]} out
|
|
288
|
+
* @param {Array<{id: string, status: string, message: string}>} health
|
|
289
|
+
*/
|
|
290
|
+
function sectionProjectHealth(out, health) {
|
|
291
|
+
out.push('');
|
|
292
|
+
out.push('## Project Health');
|
|
293
|
+
out.push('');
|
|
294
|
+
|
|
295
|
+
if (health.length === 0) {
|
|
296
|
+
out.push('No project health observations available.');
|
|
297
|
+
} else {
|
|
298
|
+
for (const h of health) {
|
|
299
|
+
let icon = 'ℹ';
|
|
300
|
+
if (h.status === 'pass') icon = '✓';
|
|
301
|
+
else if (h.status === 'warning') icon = '⚠';
|
|
302
|
+
out.push(`- ${icon} ${h.message}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
263
305
|
}
|
|
264
306
|
|
|
265
307
|
/**
|
|
@@ -275,35 +317,29 @@ function sectionEntryPoints(out, entryPoints) {
|
|
|
275
317
|
out.push('No entry points detected.');
|
|
276
318
|
} else {
|
|
277
319
|
for (const ep of entryPoints) {
|
|
278
|
-
out.push(`-
|
|
320
|
+
out.push(`- ${ep}`);
|
|
279
321
|
}
|
|
280
322
|
}
|
|
281
|
-
|
|
282
|
-
out.push('');
|
|
283
|
-
out.push('---');
|
|
284
323
|
}
|
|
285
324
|
|
|
286
325
|
/**
|
|
287
326
|
* @param {string[]} out
|
|
288
|
-
* @param {
|
|
289
|
-
* @param {
|
|
327
|
+
* @param {import('../scanner/scan.js').DirNode} tree
|
|
328
|
+
* @param {number} totalFiles
|
|
329
|
+
* @param {string} rootName
|
|
290
330
|
*/
|
|
291
|
-
function sectionFolderStructure(out,
|
|
331
|
+
function sectionFolderStructure(out, tree, totalFiles, rootName) {
|
|
332
|
+
// Contract: omit section entirely when no files were scanned.
|
|
333
|
+
if (totalFiles === 0) return;
|
|
334
|
+
|
|
292
335
|
out.push('');
|
|
293
|
-
out.push('##
|
|
336
|
+
out.push('## Structure');
|
|
294
337
|
out.push('');
|
|
295
338
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
out.push('```text');
|
|
301
|
-
out.push(tree);
|
|
302
|
-
out.push('```');
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
out.push('');
|
|
306
|
-
out.push('---');
|
|
339
|
+
const treeStr = buildTreeString(tree, rootName, totalFiles);
|
|
340
|
+
out.push('```text');
|
|
341
|
+
out.push(treeStr);
|
|
342
|
+
out.push('```');
|
|
307
343
|
}
|
|
308
344
|
|
|
309
345
|
/**
|
|
@@ -321,21 +357,6 @@ function sectionStatistics(out, result) {
|
|
|
321
357
|
['Folders', totalFolders],
|
|
322
358
|
['Scan Duration', formatDuration(scanDurationMs)],
|
|
323
359
|
]));
|
|
324
|
-
out.push('');
|
|
325
|
-
out.push('---');
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* @param {string[]} out
|
|
330
|
-
*/
|
|
331
|
-
function sectionScanInfo(out) {
|
|
332
|
-
out.push('');
|
|
333
|
-
out.push('## Scan Information');
|
|
334
|
-
out.push('');
|
|
335
|
-
out.push('Generated by **Toren**');
|
|
336
|
-
out.push('');
|
|
337
|
-
out.push('Output Format: Markdown');
|
|
338
|
-
out.push('');
|
|
339
360
|
}
|
|
340
361
|
|
|
341
362
|
// ---------------------------------------------------------------------------
|
|
@@ -352,23 +373,25 @@ function sectionScanInfo(out) {
|
|
|
352
373
|
* @param {{ cwd?: string }} [options]
|
|
353
374
|
*/
|
|
354
375
|
export function render(result, options = {}) {
|
|
355
|
-
const { rootPath, entryPoints, configs = [], scripts = [], flatFiles } = result;
|
|
376
|
+
const { rootPath, projectType, entryPoints, configs = [], scripts = [], flatFiles, tree, importantFiles = [], health = [] } = result;
|
|
356
377
|
|
|
357
378
|
const cwd = options.cwd ?? process.cwd();
|
|
358
379
|
const relRoot = path.relative(cwd, rootPath) || '.';
|
|
359
|
-
const rootName = path.basename(rootPath)
|
|
380
|
+
const rootName = relRoot === '.' ? path.basename(rootPath) : relRoot;
|
|
360
381
|
|
|
361
382
|
/** @type {string[]} */
|
|
362
383
|
const out = [];
|
|
363
384
|
|
|
364
385
|
sectionTitle(out);
|
|
365
386
|
sectionSummary(out, result, relRoot);
|
|
387
|
+
sectionProjectHealth(out, health);
|
|
388
|
+
sectionImportantFiles(out, importantFiles);
|
|
389
|
+
sectionFrameworks(out, projectType);
|
|
366
390
|
sectionEntryPoints(out, entryPoints);
|
|
367
391
|
sectionConfigurationFiles(out, configs);
|
|
368
392
|
sectionPackageScripts(out, scripts);
|
|
369
|
-
sectionFolderStructure(out, flatFiles, rootName);
|
|
370
393
|
sectionStatistics(out, result);
|
|
371
|
-
|
|
394
|
+
sectionFolderStructure(out, tree, flatFiles.length, rootName);
|
|
372
395
|
|
|
373
396
|
console.log(out.join('\n'));
|
|
374
397
|
}
|
package/src/scanner/scan.js
CHANGED
|
@@ -20,8 +20,12 @@
|
|
|
20
20
|
|
|
21
21
|
import fs from 'node:fs';
|
|
22
22
|
import path from 'node:path';
|
|
23
|
-
import { detectConfigs }
|
|
24
|
-
import { detectScripts }
|
|
23
|
+
import { detectConfigs } from '../detectors/config-detector.js';
|
|
24
|
+
import { detectScripts } from '../detectors/script-detector.js';
|
|
25
|
+
import { detectPackageManager } from '../detectors/package-manager-detector.js';
|
|
26
|
+
import { detectImportantFiles } from '../detectors/important-files-detector.js';
|
|
27
|
+
import { detectProjectInfo } from '../detectors/project-info-detector.js';
|
|
28
|
+
import { detectHealth } from '../detectors/health-detector.js';
|
|
25
29
|
|
|
26
30
|
// ---------------------------------------------------------------------------
|
|
27
31
|
// Constants
|
|
@@ -99,7 +103,8 @@ const PROJECT_TYPE_MARKERS = [
|
|
|
99
103
|
* @property {string} projectType - Detected project type label
|
|
100
104
|
* @property {Array<string>} entryPoints - Relative paths of detected entry points
|
|
101
105
|
* @property {Array<string>} configs - Relative paths of detected config files
|
|
102
|
-
* @property {Array<{name: string, command: string}>} scripts - Parsed package scripts
|
|
106
|
+
* @property {Array<{name: string, command: string, category: string|null, description: string|null, usage: string}>} scripts - Parsed and enriched package scripts
|
|
107
|
+
* @property {string|null} packageManager - Detected package manager, or null if none/ambiguous
|
|
103
108
|
* @property {DirNode} tree - Full in-memory file tree
|
|
104
109
|
* @property {Array<string>} flatFiles - All relative file paths (flat list)
|
|
105
110
|
* @property {number} totalFolders - Total number of directories walked
|
|
@@ -186,7 +191,11 @@ function walkDirectory(dirPath, rootPath, flatFiles, includeHidden, maxFiles) {
|
|
|
186
191
|
node.children.push(childNode);
|
|
187
192
|
} else if (dirent.isFile()) {
|
|
188
193
|
if (flatFiles.length >= maxFiles) {
|
|
189
|
-
|
|
194
|
+
const err = new Error(`Max file scan limit exceeded (${maxFiles} files).`);
|
|
195
|
+
err.title = 'Scan limit exceeded';
|
|
196
|
+
err.detailLabel = 'Hint';
|
|
197
|
+
err.detailValue = 'Use --max-files <number> to increase the limit.';
|
|
198
|
+
throw err;
|
|
190
199
|
}
|
|
191
200
|
const relFilePath = toPosix(path.relative(rootPath, childPath));
|
|
192
201
|
|
|
@@ -449,8 +458,18 @@ export function scan(targetPath, options = {}) {
|
|
|
449
458
|
let stat;
|
|
450
459
|
try {
|
|
451
460
|
stat = fs.statSync(rootPath);
|
|
452
|
-
} catch {
|
|
453
|
-
|
|
461
|
+
} catch (err) {
|
|
462
|
+
const error = new Error(`Path does not exist: ${rootPath}`);
|
|
463
|
+
if (err.code === 'EACCES' || err.code === 'EPERM') {
|
|
464
|
+
error.title = 'Permission denied';
|
|
465
|
+
error.message = 'You do not have permission to access the specified path.';
|
|
466
|
+
} else {
|
|
467
|
+
error.title = 'Invalid directory';
|
|
468
|
+
error.message = 'The specified path does not exist.';
|
|
469
|
+
}
|
|
470
|
+
error.detailLabel = 'Path';
|
|
471
|
+
error.detailValue = rootPath;
|
|
472
|
+
throw error;
|
|
454
473
|
}
|
|
455
474
|
|
|
456
475
|
/** @type {Array<string>} */
|
|
@@ -461,12 +480,17 @@ export function scan(targetPath, options = {}) {
|
|
|
461
480
|
let configs = [];
|
|
462
481
|
/** @type {Array<{name: string, command: string}>} */
|
|
463
482
|
let scripts = [];
|
|
483
|
+
/** @type {string|null} */
|
|
484
|
+
let packageManager = null;
|
|
485
|
+
let importantFiles = [];
|
|
486
|
+
let projectInfo = null;
|
|
487
|
+
let health = [];
|
|
464
488
|
|
|
465
489
|
const startTime = performance.now();
|
|
466
490
|
let tree;
|
|
467
491
|
let projectType = 'Unknown';
|
|
468
492
|
let totalFolders = 0;
|
|
469
|
-
|
|
493
|
+
|
|
470
494
|
if (stat.isDirectory()) {
|
|
471
495
|
tree = walkDirectory(rootPath, rootPath, flatFiles, includeHidden, maxFiles);
|
|
472
496
|
projectType = detectProjectType(rootPath);
|
|
@@ -474,7 +498,11 @@ export function scan(targetPath, options = {}) {
|
|
|
474
498
|
totalFolders = countFolders(tree) - 1;
|
|
475
499
|
entryPoints = findEntryPoints(projectType, flatFiles, rootPath);
|
|
476
500
|
configs = detectConfigs(flatFiles).configs;
|
|
477
|
-
|
|
501
|
+
packageManager = detectPackageManager(flatFiles).packageManager;
|
|
502
|
+
scripts = detectScripts(rootPath, packageManager).scripts;
|
|
503
|
+
importantFiles = detectImportantFiles({ flatFiles, projectType, entryPoints, configs }).importantFiles;
|
|
504
|
+
projectInfo = detectProjectInfo({ rootPath, projectType, flatFiles, entryPoints, packageManager, scripts }).projectInfo;
|
|
505
|
+
health = detectHealth({ flatFiles, configs, importantFiles, scripts, projectType }).health;
|
|
478
506
|
} else if (stat.isFile()) {
|
|
479
507
|
const relFilePath = toPosix(path.basename(rootPath));
|
|
480
508
|
tree = {
|
|
@@ -492,9 +520,18 @@ export function scan(targetPath, options = {}) {
|
|
|
492
520
|
flatFiles.push(relFilePath);
|
|
493
521
|
entryPoints = [relFilePath]; // A single file is its own entry point
|
|
494
522
|
configs = detectConfigs(flatFiles).configs;
|
|
495
|
-
|
|
523
|
+
packageManager = detectPackageManager(flatFiles).packageManager;
|
|
524
|
+
scripts = detectScripts(path.dirname(rootPath), packageManager).scripts;
|
|
525
|
+
importantFiles = detectImportantFiles({ flatFiles, projectType, entryPoints, configs }).importantFiles;
|
|
526
|
+
projectInfo = detectProjectInfo({ rootPath, projectType, flatFiles, entryPoints, packageManager, scripts }).projectInfo;
|
|
527
|
+
health = detectHealth({ flatFiles, configs, importantFiles, scripts, projectType }).health;
|
|
496
528
|
} else {
|
|
497
|
-
|
|
529
|
+
const err = new Error(`Path is neither a file nor a directory: ${rootPath}`);
|
|
530
|
+
err.title = 'Unsupported path type';
|
|
531
|
+
err.message = 'The specified path is neither a file nor a directory.';
|
|
532
|
+
err.detailLabel = 'Path';
|
|
533
|
+
err.detailValue = rootPath;
|
|
534
|
+
throw err;
|
|
498
535
|
}
|
|
499
536
|
|
|
500
537
|
const scanDurationMs = performance.now() - startTime;
|
|
@@ -505,6 +542,10 @@ export function scan(targetPath, options = {}) {
|
|
|
505
542
|
entryPoints,
|
|
506
543
|
configs,
|
|
507
544
|
scripts,
|
|
545
|
+
packageManager,
|
|
546
|
+
importantFiles,
|
|
547
|
+
projectInfo,
|
|
548
|
+
health,
|
|
508
549
|
tree,
|
|
509
550
|
flatFiles,
|
|
510
551
|
totalFolders,
|