@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.
Files changed (64) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cli/toren.d.ts +24 -0
  3. package/dist/cli/toren.js +254 -0
  4. package/dist/cli/toren.js.map +1 -0
  5. package/dist/detectors/config-detector.d.ts +3 -0
  6. package/dist/detectors/config-detector.js +58 -0
  7. package/dist/detectors/config-detector.js.map +1 -0
  8. package/dist/detectors/health-detector.d.ts +12 -0
  9. package/dist/detectors/health-detector.js +53 -0
  10. package/dist/detectors/health-detector.js.map +1 -0
  11. package/dist/detectors/important-files-detector.d.ts +51 -0
  12. package/dist/detectors/important-files-detector.js +455 -0
  13. package/dist/detectors/important-files-detector.js.map +1 -0
  14. package/dist/detectors/package-manager-detector.d.ts +7 -0
  15. package/dist/detectors/package-manager-detector.js +35 -0
  16. package/dist/detectors/package-manager-detector.js.map +1 -0
  17. package/dist/detectors/project-info-detector.d.ts +13 -0
  18. package/dist/detectors/project-info-detector.js +97 -0
  19. package/dist/detectors/project-info-detector.js.map +1 -0
  20. package/dist/detectors/script-detector.d.ts +42 -0
  21. package/dist/detectors/script-detector.js +199 -0
  22. package/dist/detectors/script-detector.js.map +1 -0
  23. package/dist/focused-output.d.ts +27 -0
  24. package/dist/focused-output.js +201 -0
  25. package/dist/focused-output.js.map +1 -0
  26. package/dist/lifecycle.d.ts +2 -0
  27. package/dist/lifecycle.js +114 -0
  28. package/dist/lifecycle.js.map +1 -0
  29. package/dist/renderers/console-renderer.d.ts +19 -0
  30. package/dist/renderers/console-renderer.js +361 -0
  31. package/dist/renderers/console-renderer.js.map +1 -0
  32. package/dist/renderers/html-renderer.d.ts +14 -0
  33. package/{src → dist}/renderers/html-renderer.js +181 -241
  34. package/dist/renderers/html-renderer.js.map +1 -0
  35. package/dist/renderers/index.d.ts +6 -0
  36. package/dist/renderers/index.js +12 -0
  37. package/dist/renderers/index.js.map +1 -0
  38. package/dist/renderers/json-renderer.d.ts +10 -0
  39. package/dist/renderers/json-renderer.js +127 -0
  40. package/dist/renderers/json-renderer.js.map +1 -0
  41. package/dist/renderers/markdown-renderer.d.ts +13 -0
  42. package/dist/renderers/markdown-renderer.js +360 -0
  43. package/dist/renderers/markdown-renderer.js.map +1 -0
  44. package/dist/scanner/scan.d.ts +2 -0
  45. package/dist/scanner/scan.js +573 -0
  46. package/dist/scanner/scan.js.map +1 -0
  47. package/dist/types/index.d.ts +85 -0
  48. package/dist/types/index.js +2 -0
  49. package/dist/types/index.js.map +1 -0
  50. package/package.json +11 -6
  51. package/bin/toren.js +0 -311
  52. package/src/detectors/config-detector.js +0 -81
  53. package/src/detectors/health-detector.js +0 -72
  54. package/src/detectors/important-files-detector.js +0 -515
  55. package/src/detectors/package-manager-detector.js +0 -112
  56. package/src/detectors/project-info-detector.js +0 -89
  57. package/src/detectors/script-detector.js +0 -248
  58. package/src/focused-output.js +0 -216
  59. package/src/lifecycle.js +0 -124
  60. package/src/renderers/console-renderer.js +0 -399
  61. package/src/renderers/index.js +0 -41
  62. package/src/renderers/json-renderer.js +0 -168
  63. package/src/renderers/markdown-renderer.js +0 -397
  64. package/src/scanner/scan.js +0 -554
@@ -1,554 +0,0 @@
1
- /**
2
- * @fileoverview Toren CLI — Codebase Scanner (Core Logic)
3
- *
4
- * Responsibilities:
5
- * - Recursively walk a project directory
6
- * - Ignore irrelevant paths (node_modules, .git, dist, build, etc.)
7
- * - Build an in-memory file-tree representation
8
- * - Detect the project type from marker files
9
- * - Identify known entry-point files
10
- *
11
- * This module is intentionally free of side-effects (no console.log),
12
- * except for warnings about unreadable directories.
13
- * All output concerns live in bin/toren.js.
14
- *
15
- * Designed to scale into:
16
- * - Module / dependency graph analysis
17
- * - Architecture visualisation layers
18
- * - AI explanation integrations
19
- */
20
-
21
- import fs from 'node:fs';
22
- import path from 'node:path';
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';
29
-
30
- // ---------------------------------------------------------------------------
31
- // Constants
32
- // ---------------------------------------------------------------------------
33
-
34
- /**
35
- * Directory / file names that are never walked.
36
- * Stored as a Set for O(1) membership checks.
37
- * @type {Set<string>}
38
- */
39
- const IGNORED_DIRS = new Set([
40
- 'node_modules',
41
- '.git',
42
- 'dist',
43
- 'build',
44
- '.cache',
45
- '.next',
46
- '.nuxt',
47
- 'out',
48
- 'coverage',
49
- '__pycache__',
50
- '.venv',
51
- 'venv',
52
- '.idea',
53
- '.vscode',
54
- '.svelte-kit',
55
- '.turbo',
56
- '.parcel-cache',
57
- '.DS_Store'
58
- ]);
59
-
60
- /**
61
- * Mapping from a marker filename to a human-readable project-type label.
62
- * Evaluated in iteration order — more specific entries should come first.
63
- * @type {Array<{ marker: string, label: string }>}
64
- */
65
- const PROJECT_TYPE_MARKERS = [
66
- { marker: 'package.json', label: 'Node.js / JavaScript' },
67
- { marker: 'pom.xml', label: 'Java / Spring Boot' },
68
- { marker: 'build.gradle', label: 'Java / Gradle' },
69
- { marker: 'requirements.txt', label: 'Python' },
70
- { marker: 'Pipfile', label: 'Python (Pipenv)' },
71
- { marker: 'pyproject.toml', label: 'Python (pyproject)' },
72
- { marker: 'go.mod', label: 'Go' },
73
- { marker: 'Cargo.toml', label: 'Rust' },
74
- { marker: 'composer.json', label: 'PHP / Composer' },
75
- { marker: 'Gemfile', label: 'Ruby' },
76
- { marker: 'mix.exs', label: 'Elixir' },
77
- ];
78
-
79
- // ---------------------------------------------------------------------------
80
- // Types (JSDoc — no TypeScript dependency required)
81
- // ---------------------------------------------------------------------------
82
-
83
- /**
84
- * @typedef {Object} FileNode
85
- * @property {'file'} type
86
- * @property {string} name - Basename of the file
87
- * @property {string} fullPath - Absolute path
88
- * @property {string} relPath - Path relative to the scanned root (POSIX style)
89
- */
90
-
91
- /**
92
- * @typedef {Object} DirNode
93
- * @property {'directory'} type
94
- * @property {string} name - Basename of the directory
95
- * @property {string} fullPath - Absolute path
96
- * @property {string} relPath - Path relative to the scanned root (POSIX style)
97
- * @property {Array<FileNode|DirNode>} children
98
- */
99
-
100
- /**
101
- * @typedef {Object} ScanResult
102
- * @property {string} rootPath - Absolute path that was scanned
103
- * @property {string} projectType - Detected project type label
104
- * @property {Array<string>} entryPoints - Relative paths of detected entry points
105
- * @property {Array<string>} configs - Relative paths of detected config files
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
108
- * @property {DirNode} tree - Full in-memory file tree
109
- * @property {Array<string>} flatFiles - All relative file paths (flat list)
110
- * @property {number} totalFolders - Total number of directories walked
111
- * @property {number} scanDurationMs - Wall-clock time of the scan in milliseconds
112
- */
113
-
114
- // ---------------------------------------------------------------------------
115
- // Internal helpers
116
- // ---------------------------------------------------------------------------
117
-
118
- /**
119
- * Normalize a path to use POSIX separators ('/').
120
- */
121
- function toPosix(p) {
122
- return p.replace(/\\/g, '/');
123
- }
124
-
125
- /**
126
- * Determine whether a directory entry should be skipped.
127
- *
128
- * @param {string} name - Basename of the entry
129
- * @param {fs.Dirent} dirent
130
- * @param {boolean} includeHidden
131
- * @returns {boolean}
132
- */
133
- function shouldIgnore(name, dirent, includeHidden) {
134
- if (!includeHidden && name.startsWith('.')) return true;
135
- return IGNORED_DIRS.has(name);
136
- }
137
-
138
- /**
139
- * Recursively walk `dirPath`, building a DirNode tree.
140
- * Also populates `flatFiles` array by reference.
141
- *
142
- * @param {string} dirPath - Absolute path of the current directory
143
- * @param {string} rootPath - Absolute path of the scan root (for relative paths)
144
- * @param {Array<string>} flatFiles - Accumulator for all relative file paths
145
- * @param {boolean} includeHidden - Whether to include hidden files
146
- * @param {number} maxFiles - Maximum number of files to scan before aborting
147
- * @returns {DirNode}
148
- */
149
- function walkDirectory(dirPath, rootPath, flatFiles, includeHidden, maxFiles) {
150
- const name = path.basename(dirPath);
151
- let rawRelPath = path.relative(rootPath, dirPath) || '.';
152
- const relPath = toPosix(rawRelPath);
153
-
154
- /** @type {DirNode} */
155
- const node = {
156
- type: 'directory',
157
- name,
158
- fullPath: dirPath,
159
- relPath,
160
- children: [],
161
- };
162
-
163
- let entries;
164
- try {
165
- entries = fs.readdirSync(dirPath, { withFileTypes: true });
166
- } catch {
167
- // Permission-denied or unreadable directory — skip silently.
168
- // Write to stderr so stdout remains clean for piped/redirected output.
169
- process.stderr.write(`[toren] warning: skipping unreadable directory: ${dirPath}\n`);
170
- return node;
171
- }
172
-
173
- // Sort: directories first, then files — both alphabetically.
174
- entries.sort((a, b) => {
175
- const aIsDir = a.isDirectory() ? 0 : 1;
176
- const bIsDir = b.isDirectory() ? 0 : 1;
177
- if (aIsDir !== bIsDir) return aIsDir - bIsDir;
178
- return a.name.localeCompare(b.name);
179
- });
180
-
181
- for (const dirent of entries) {
182
- if (shouldIgnore(dirent.name, dirent, includeHidden)) continue;
183
-
184
- const childPath = path.join(dirPath, dirent.name);
185
-
186
- // Explicitly skip symbolic links to prevent infinite loops and unsafe traversals
187
- if (dirent.isSymbolicLink()) continue;
188
-
189
- if (dirent.isDirectory()) {
190
- const childNode = walkDirectory(childPath, rootPath, flatFiles, includeHidden, maxFiles);
191
- node.children.push(childNode);
192
- } else if (dirent.isFile()) {
193
- if (flatFiles.length >= maxFiles) {
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;
199
- }
200
- const relFilePath = toPosix(path.relative(rootPath, childPath));
201
-
202
- /** @type {FileNode} */
203
- const fileNode = {
204
- type: 'file',
205
- name: dirent.name,
206
- fullPath: childPath,
207
- relPath: relFilePath,
208
- };
209
-
210
- node.children.push(fileNode);
211
- flatFiles.push(relFilePath);
212
- }
213
- }
214
-
215
- return node;
216
- }
217
-
218
- /**
219
- * Detect the project type by checking for known marker files in `rootPath`.
220
- *
221
- * Returns the label of the first matched marker, or `'Unknown'` if none match.
222
- *
223
- * @param {string} rootPath - Absolute path to the project root
224
- * @returns {string}
225
- */
226
- function detectProjectType(rootPath) {
227
- for (const { marker, label } of PROJECT_TYPE_MARKERS) {
228
- const markerPath = path.join(rootPath, marker);
229
- if (fs.existsSync(markerPath)) {
230
- // Refine Node.js projects by inspecting package.json dependencies.
231
- if (marker === 'package.json') {
232
- return refineNodeProjectType(markerPath);
233
- }
234
- return label;
235
- }
236
- }
237
- return 'Unknown';
238
- }
239
-
240
- /**
241
- * Read `package.json` and return a more specific label when React / Next / Vue
242
- * etc. are listed as dependencies.
243
- *
244
- * @param {string} pkgPath - Absolute path to package.json
245
- * @returns {string}
246
- */
247
- function refineNodeProjectType(pkgPath) {
248
- try {
249
- const raw = fs.readFileSync(pkgPath, 'utf8');
250
- const pkg = JSON.parse(raw);
251
- const deps = {
252
- ...pkg.dependencies,
253
- ...pkg.devDependencies,
254
- ...pkg.peerDependencies,
255
- };
256
-
257
- if (deps['next']) return 'Next.js';
258
- if (deps['nuxt'] || deps['nuxt3']) return 'Nuxt.js';
259
- if (deps['react']) return 'React';
260
- if (deps['vue']) return 'Vue.js';
261
- if (deps['@angular/core']) return 'Angular';
262
- if (deps['svelte']) return 'Svelte';
263
- if (deps['express']) return 'Node.js / Express';
264
- if (deps['fastify']) return 'Node.js / Fastify';
265
- if (deps['koa']) return 'Node.js / Koa';
266
- if (deps['typescript']) return 'Node.js / TypeScript';
267
- } catch {
268
- // Malformed package.json — fall through.
269
- }
270
- return 'Node.js / JavaScript';
271
- }
272
-
273
- // ---------------------------------------------------------------------------
274
- // Entry Point Heuristics
275
- // ---------------------------------------------------------------------------
276
-
277
- const FALSE_POSITIVES = [
278
- '/internal/', '/renderer/', '/renderers/', '/dist/', '/build/', '/generated/', '/node_modules/'
279
- ];
280
-
281
- function isFalsePositive(relPath) {
282
- const normalized = '/' + relPath + '/'; // relPath is already POSIX
283
- if (FALSE_POSITIVES.some(fp => normalized.includes(fp))) return true;
284
- if (relPath.includes('.test.') || relPath.includes('.spec.')) return true;
285
- // Exclude config files usually not entry points
286
- if (relPath.endsWith('.config.js') || relPath.endsWith('.config.ts')) return true;
287
- return false;
288
- }
289
-
290
- function findEntryPoints(projectType, flatFiles, rootPath) {
291
- let entries = [];
292
- const validFiles = flatFiles.filter(f => !isFalsePositive(f));
293
- const validSet = new Set(validFiles);
294
-
295
- if (projectType === 'Node.js / JavaScript' || projectType.startsWith('Node.js')) {
296
- try {
297
- const pkgPath = path.join(rootPath, 'package.json');
298
- if (fs.existsSync(pkgPath)) {
299
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
300
- if (pkg.bin) {
301
- if (typeof pkg.bin === 'string') entries.push(pkg.bin);
302
- else Object.values(pkg.bin).forEach(b => entries.push(b));
303
- }
304
- if (pkg.main) entries.push(pkg.main);
305
- }
306
- } catch {}
307
-
308
- // Normalize and filter package.json entries to ensure they exist
309
- entries = entries.map(e => toPosix(e).replace(/^\.\//, '')).filter(e => validSet.has(e));
310
-
311
- if (entries.length === 0) {
312
- const fallbacks = ['src/index.ts', 'src/index.js', 'lib/index.js', 'index.js'];
313
- for (const f of fallbacks) {
314
- if (validSet.has(f)) { entries.push(f); break; }
315
- }
316
- }
317
- } else if (
318
- projectType === 'React' || projectType === 'Next.js' ||
319
- projectType === 'Nuxt.js' || projectType === 'Vue.js' ||
320
- projectType === 'Angular' || projectType === 'Svelte'
321
- ) {
322
- const priorities = [
323
- 'src/main.tsx', 'src/main.jsx', 'src/main.ts', 'src/main.js',
324
- 'pages/_app.tsx', 'pages/_app.js',
325
- 'app/layout.tsx', 'app/layout.js',
326
- 'src/App.tsx', 'src/App.jsx',
327
- 'index.html',
328
- ];
329
- for (const p of priorities) {
330
- if (validSet.has(p)) { entries.push(p); break; }
331
- }
332
- } else if (projectType.includes('Java')) {
333
- const applicationJava = validFiles.filter(f => f.endsWith('Application.java'));
334
- if (applicationJava.length > 0) {
335
- entries.push(...applicationJava);
336
- } else {
337
- for (const f of validFiles) {
338
- if (f.endsWith('.java')) {
339
- try {
340
- const content = fs.readFileSync(path.join(rootPath, f), 'utf8');
341
- if (content.includes('public static void main')) {
342
- entries.push(f);
343
- break;
344
- }
345
- } catch {}
346
- }
347
- }
348
- }
349
- } else if (projectType.includes('Python')) {
350
- const priorities = ['main.py', 'app.py', '__main__.py', 'manage.py', 'run.py'];
351
- for (const p of priorities) {
352
- if (validSet.has(p)) { entries.push(p); break; }
353
- }
354
- if (entries.length === 0) {
355
- for (const f of validFiles) {
356
- if (f.endsWith('.py')) {
357
- try {
358
- const content = fs.readFileSync(path.join(rootPath, f), 'utf8');
359
- if (content.includes('if __name__ == "__main__":') || content.includes("if __name__ == '__main__':")) {
360
- entries.push(f);
361
- break;
362
- }
363
- } catch {}
364
- }
365
- }
366
- }
367
- } else if (projectType === 'Go') {
368
- const priorities = ['main.go', 'cmd/main.go', 'cmd/api/main.go'];
369
- for (const p of priorities) {
370
- if (validSet.has(p)) { entries.push(p); break; }
371
- }
372
- } else if (projectType === 'Rust') {
373
- const priorities = ['src/main.rs', 'src/lib.rs'];
374
- for (const p of priorities) {
375
- if (validSet.has(p)) { entries.push(p); break; }
376
- }
377
- } else if (projectType === 'Ruby') {
378
- const priorities = ['app.rb', 'main.rb', 'config.ru', 'Rakefile'];
379
- for (const p of priorities) {
380
- if (validSet.has(p)) { entries.push(p); break; }
381
- }
382
- } else if (projectType === 'PHP / Composer') {
383
- const priorities = ['index.php', 'public/index.php', 'src/index.php', 'artisan'];
384
- for (const p of priorities) {
385
- if (validSet.has(p)) { entries.push(p); break; }
386
- }
387
- } else if (projectType === 'Elixir') {
388
- const priorities = ['lib/mix/tasks/run.ex', 'mix.exs'];
389
- for (const p of priorities) {
390
- if (validSet.has(p)) { entries.push(p); break; }
391
- }
392
- }
393
-
394
- if (entries.length === 0) {
395
- // Fallbacks for Unknown or unrecognised projects.
396
- // First try well-known index file patterns.
397
- for (const f of validFiles) {
398
- if (/^(src\/)?index\.[a-z]+$/.test(f)) {
399
- entries.push(f);
400
- break;
401
- }
402
- }
403
- // If still empty, pick the file with the highest import/export density.
404
- if (entries.length === 0) {
405
- let best = null;
406
- let maxScore = -1;
407
- for (const f of validFiles) {
408
- if (f.match(/\.(js|ts|jsx|tsx|py|java|go|rs|rb|php)$/)) {
409
- try {
410
- const content = fs.readFileSync(path.join(rootPath, f), 'utf8');
411
- const score = (content.match(/import /g) || []).length +
412
- (content.match(/export /g) || []).length +
413
- (content.match(/require\(/g) || []).length;
414
- if (score > maxScore) {
415
- maxScore = score;
416
- best = f;
417
- }
418
- } catch {}
419
- }
420
- }
421
- if (best) entries.push(best);
422
- }
423
- }
424
-
425
- return Array.from(new Set(entries));
426
- }
427
-
428
- // ---------------------------------------------------------------------------
429
- // Tree helpers
430
- // ---------------------------------------------------------------------------
431
-
432
- /**
433
- * Count the total number of directory nodes in a tree (including the root).
434
- *
435
- * @param {DirNode} node
436
- * @returns {number}
437
- */
438
- function countFolders(node) {
439
- let count = 1; // count this directory
440
- for (const child of node.children ?? []) {
441
- if (child.type === 'directory') {
442
- count += countFolders(child);
443
- }
444
- }
445
- return count;
446
- }
447
-
448
- // ---------------------------------------------------------------------------
449
- // Public API
450
- // ---------------------------------------------------------------------------
451
-
452
- export function scan(targetPath, options = {}) {
453
- const rootPath = path.resolve(targetPath);
454
- const includeHidden = !!options.includeHidden;
455
- const maxFiles = options.maxFiles || 50000; // Default cap of 50k files for huge repos
456
-
457
- // Validate target
458
- let stat;
459
- try {
460
- stat = fs.statSync(rootPath);
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;
473
- }
474
-
475
- /** @type {Array<string>} */
476
- const flatFiles = [];
477
- /** @type {Array<string>} */
478
- let entryPoints = [];
479
- /** @type {Array<string>} */
480
- let configs = [];
481
- /** @type {Array<{name: string, command: string}>} */
482
- let scripts = [];
483
- /** @type {string|null} */
484
- let packageManager = null;
485
- let importantFiles = [];
486
- let projectInfo = null;
487
- let health = [];
488
-
489
- const startTime = performance.now();
490
- let tree;
491
- let projectType = 'Unknown';
492
- let totalFolders = 0;
493
-
494
- if (stat.isDirectory()) {
495
- tree = walkDirectory(rootPath, rootPath, flatFiles, includeHidden, maxFiles);
496
- projectType = detectProjectType(rootPath);
497
- // Count all directory nodes in the tree (excluding root itself).
498
- totalFolders = countFolders(tree) - 1;
499
- entryPoints = findEntryPoints(projectType, flatFiles, rootPath);
500
- configs = detectConfigs(flatFiles).configs;
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;
506
- } else if (stat.isFile()) {
507
- const relFilePath = toPosix(path.basename(rootPath));
508
- tree = {
509
- type: 'directory',
510
- name: path.basename(path.dirname(rootPath)),
511
- fullPath: path.dirname(rootPath),
512
- relPath: '.',
513
- children: [{
514
- type: 'file',
515
- name: relFilePath,
516
- fullPath: rootPath,
517
- relPath: relFilePath
518
- }]
519
- };
520
- flatFiles.push(relFilePath);
521
- entryPoints = [relFilePath]; // A single file is its own entry point
522
- configs = detectConfigs(flatFiles).configs;
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;
528
- } else {
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;
535
- }
536
-
537
- const scanDurationMs = performance.now() - startTime;
538
-
539
- return {
540
- rootPath,
541
- projectType,
542
- entryPoints,
543
- configs,
544
- scripts,
545
- packageManager,
546
- importantFiles,
547
- projectInfo,
548
- health,
549
- tree,
550
- flatFiles,
551
- totalFolders,
552
- scanDurationMs,
553
- };
554
- }