analyze-codebase 1.2.2 ā 1.3.1
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/.claude/settings.local.json +16 -0
- package/.vscode/settings.json +5 -0
- package/CHANGELOG.md +137 -0
- package/ENHANCEMENTS.md +257 -0
- package/FEATURES.md +225 -0
- package/dist/analyze/i18n/index.js +477 -0
- package/dist/analyze/index.js +187 -25
- package/dist/index.js +203 -16
- package/dist/utils/__tests__/config.test.js +94 -0
- package/dist/utils/__tests__/progress.test.js +37 -0
- package/dist/utils/cancellation.js +75 -0
- package/dist/utils/config.js +83 -0
- package/dist/utils/export.js +218 -0
- package/dist/utils/output.js +137 -41
- package/dist/utils/progress.js +51 -0
- package/dist/utils/spinner.js +61 -0
- package/dist/utils/watch.js +99 -0
- package/docs/agents/developer-agent.md +818 -0
- package/docs/agents/research-agent.md +244 -0
- package/package.json +44 -20
- package/readme.md +163 -86
- package/tsconfig.json +5 -2
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resetCancellation = exports.CancellationToken = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Cancellation token for graceful shutdown
|
|
6
|
+
* Uses a singleton pattern to ensure only one signal handler is registered
|
|
7
|
+
*/
|
|
8
|
+
let globalCancelled = false;
|
|
9
|
+
let signalHandlerRegistered = false;
|
|
10
|
+
const listeners = [];
|
|
11
|
+
const handleSignal = (signal) => {
|
|
12
|
+
if (!globalCancelled) {
|
|
13
|
+
globalCancelled = true;
|
|
14
|
+
// Immediately stop progress bars and spinners
|
|
15
|
+
listeners.forEach((listener) => {
|
|
16
|
+
try {
|
|
17
|
+
listener();
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
// Ignore errors in cleanup
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
// Force immediate exit for SIGINT
|
|
24
|
+
if (signal === "SIGINT") {
|
|
25
|
+
console.log("\n"); // New line after ^C
|
|
26
|
+
process.exit(130);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
class CancellationToken {
|
|
31
|
+
constructor() {
|
|
32
|
+
this.listeners = [];
|
|
33
|
+
// Register signal handlers only once
|
|
34
|
+
if (!signalHandlerRegistered) {
|
|
35
|
+
// Handle SIGINT (Ctrl+C) - immediate exit
|
|
36
|
+
process.on("SIGINT", () => {
|
|
37
|
+
handleSignal("SIGINT");
|
|
38
|
+
// Exit immediately - no delay
|
|
39
|
+
process.exit(130); // Standard exit code for SIGINT
|
|
40
|
+
});
|
|
41
|
+
// Handle SIGTERM
|
|
42
|
+
process.on("SIGTERM", () => {
|
|
43
|
+
handleSignal("SIGTERM");
|
|
44
|
+
setTimeout(() => {
|
|
45
|
+
process.exit(143); // Standard exit code for SIGTERM
|
|
46
|
+
}, 1000);
|
|
47
|
+
});
|
|
48
|
+
signalHandlerRegistered = true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
isCancelled() {
|
|
52
|
+
return globalCancelled;
|
|
53
|
+
}
|
|
54
|
+
cancel() {
|
|
55
|
+
handleSignal("manual");
|
|
56
|
+
}
|
|
57
|
+
onCancel(callback) {
|
|
58
|
+
this.listeners.push(callback);
|
|
59
|
+
listeners.push(callback);
|
|
60
|
+
}
|
|
61
|
+
throwIfCancelled() {
|
|
62
|
+
if (globalCancelled) {
|
|
63
|
+
throw new Error("Operation cancelled by user");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.CancellationToken = CancellationToken;
|
|
68
|
+
/**
|
|
69
|
+
* Reset cancellation state (useful for testing)
|
|
70
|
+
*/
|
|
71
|
+
const resetCancellation = () => {
|
|
72
|
+
globalCancelled = false;
|
|
73
|
+
listeners.length = 0;
|
|
74
|
+
};
|
|
75
|
+
exports.resetCancellation = resetCancellation;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.displayConfigInfo = exports.createConfigFile = exports.loadConfig = exports.findConfigFile = void 0;
|
|
30
|
+
const fs = __importStar(require("fs/promises"));
|
|
31
|
+
const path = __importStar(require("path"));
|
|
32
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
33
|
+
const boxen_1 = __importDefault(require("boxen"));
|
|
34
|
+
const CONFIG_FILE_NAME = ".analyze-codebase.json";
|
|
35
|
+
const findConfigFile = async (directory) => {
|
|
36
|
+
let currentDir = path.resolve(directory);
|
|
37
|
+
while (currentDir !== path.dirname(currentDir)) {
|
|
38
|
+
const configPath = path.join(currentDir, CONFIG_FILE_NAME);
|
|
39
|
+
try {
|
|
40
|
+
await fs.access(configPath);
|
|
41
|
+
return configPath;
|
|
42
|
+
}
|
|
43
|
+
catch (_a) {
|
|
44
|
+
// Config file doesn't exist, continue searching
|
|
45
|
+
}
|
|
46
|
+
currentDir = path.dirname(currentDir);
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
};
|
|
50
|
+
exports.findConfigFile = findConfigFile;
|
|
51
|
+
const loadConfig = async (directory) => {
|
|
52
|
+
const configPath = await (0, exports.findConfigFile)(directory);
|
|
53
|
+
if (!configPath) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const configContent = await fs.readFile(configPath, "utf-8");
|
|
58
|
+
const config = JSON.parse(configContent);
|
|
59
|
+
return config;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error(chalk_1.default.red(`\nError reading config file: ${configPath}\n${error}\n`));
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
exports.loadConfig = loadConfig;
|
|
67
|
+
const createConfigFile = async (directory, config) => {
|
|
68
|
+
const configPath = path.join(directory, CONFIG_FILE_NAME);
|
|
69
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
70
|
+
return configPath;
|
|
71
|
+
};
|
|
72
|
+
exports.createConfigFile = createConfigFile;
|
|
73
|
+
const displayConfigInfo = (configPath) => {
|
|
74
|
+
if (configPath) {
|
|
75
|
+
const message = (0, boxen_1.default)(chalk_1.default.cyan(`š Using config: ${configPath}`), {
|
|
76
|
+
padding: 1,
|
|
77
|
+
borderColor: "cyan",
|
|
78
|
+
borderStyle: "round",
|
|
79
|
+
});
|
|
80
|
+
console.log(message);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
exports.displayConfigInfo = displayConfigInfo;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.exportResults = void 0;
|
|
27
|
+
const fs = __importStar(require("fs/promises"));
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
const exportResults = async (result, outputPath, format = "json") => {
|
|
30
|
+
const fullPath = path.resolve(outputPath);
|
|
31
|
+
switch (format) {
|
|
32
|
+
case "json":
|
|
33
|
+
return await exportJSON(result, fullPath);
|
|
34
|
+
case "csv":
|
|
35
|
+
return await exportCSV(result, fullPath);
|
|
36
|
+
case "html":
|
|
37
|
+
return await exportHTML(result, fullPath);
|
|
38
|
+
default:
|
|
39
|
+
throw new Error(`Unsupported export format: ${format}`);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
exports.exportResults = exportResults;
|
|
43
|
+
const exportJSON = async (result, outputPath) => {
|
|
44
|
+
const jsonContent = JSON.stringify(result, null, 2);
|
|
45
|
+
await fs.writeFile(outputPath, jsonContent, "utf-8");
|
|
46
|
+
return outputPath;
|
|
47
|
+
};
|
|
48
|
+
const exportCSV = async (result, outputPath) => {
|
|
49
|
+
const lines = [];
|
|
50
|
+
// Header
|
|
51
|
+
lines.push("Type,Count,Percentage");
|
|
52
|
+
// File name cases
|
|
53
|
+
if (result.fileNameCases) {
|
|
54
|
+
Object.entries(result.fileNameCases).forEach(([key, value]) => {
|
|
55
|
+
const percentage = ((value / result.fileCount) * 100).toFixed(2);
|
|
56
|
+
lines.push(`File Name: ${key},${value},${percentage}%`);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// Content types
|
|
60
|
+
Object.entries(result.output).forEach(([key, value]) => {
|
|
61
|
+
const percentage = ((value / result.output.Physical) * 100).toFixed(2);
|
|
62
|
+
lines.push(`Content: ${key},${value},${percentage}%`);
|
|
63
|
+
});
|
|
64
|
+
await fs.writeFile(outputPath, lines.join("\n"), "utf-8");
|
|
65
|
+
return outputPath;
|
|
66
|
+
};
|
|
67
|
+
const exportHTML = async (result, outputPath) => {
|
|
68
|
+
var _a, _b;
|
|
69
|
+
const html = `
|
|
70
|
+
<!DOCTYPE html>
|
|
71
|
+
<html lang="en">
|
|
72
|
+
<head>
|
|
73
|
+
<meta charset="UTF-8">
|
|
74
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
75
|
+
<title>Codebase Analysis Report</title>
|
|
76
|
+
<style>
|
|
77
|
+
body {
|
|
78
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
79
|
+
max-width: 1200px;
|
|
80
|
+
margin: 0 auto;
|
|
81
|
+
padding: 20px;
|
|
82
|
+
background: #f5f5f5;
|
|
83
|
+
}
|
|
84
|
+
.header {
|
|
85
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
86
|
+
color: white;
|
|
87
|
+
padding: 30px;
|
|
88
|
+
border-radius: 10px;
|
|
89
|
+
margin-bottom: 30px;
|
|
90
|
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
91
|
+
}
|
|
92
|
+
.header h1 {
|
|
93
|
+
margin: 0;
|
|
94
|
+
font-size: 2.5em;
|
|
95
|
+
}
|
|
96
|
+
.header .meta {
|
|
97
|
+
margin-top: 10px;
|
|
98
|
+
opacity: 0.9;
|
|
99
|
+
}
|
|
100
|
+
.section {
|
|
101
|
+
background: white;
|
|
102
|
+
padding: 25px;
|
|
103
|
+
border-radius: 10px;
|
|
104
|
+
margin-bottom: 20px;
|
|
105
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
106
|
+
}
|
|
107
|
+
.section h2 {
|
|
108
|
+
margin-top: 0;
|
|
109
|
+
color: #333;
|
|
110
|
+
border-bottom: 2px solid #667eea;
|
|
111
|
+
padding-bottom: 10px;
|
|
112
|
+
}
|
|
113
|
+
table {
|
|
114
|
+
width: 100%;
|
|
115
|
+
border-collapse: collapse;
|
|
116
|
+
margin-top: 15px;
|
|
117
|
+
}
|
|
118
|
+
th, td {
|
|
119
|
+
padding: 12px;
|
|
120
|
+
text-align: left;
|
|
121
|
+
border-bottom: 1px solid #ddd;
|
|
122
|
+
}
|
|
123
|
+
th {
|
|
124
|
+
background: #f8f9fa;
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
color: #495057;
|
|
127
|
+
}
|
|
128
|
+
tr:hover {
|
|
129
|
+
background: #f8f9fa;
|
|
130
|
+
}
|
|
131
|
+
.stat {
|
|
132
|
+
display: inline-block;
|
|
133
|
+
background: #667eea;
|
|
134
|
+
color: white;
|
|
135
|
+
padding: 8px 16px;
|
|
136
|
+
border-radius: 20px;
|
|
137
|
+
margin: 5px;
|
|
138
|
+
font-weight: 600;
|
|
139
|
+
}
|
|
140
|
+
.percentage {
|
|
141
|
+
color: #28a745;
|
|
142
|
+
font-weight: 600;
|
|
143
|
+
}
|
|
144
|
+
</style>
|
|
145
|
+
</head>
|
|
146
|
+
<body>
|
|
147
|
+
<div class="header">
|
|
148
|
+
<h1>š Codebase Analysis Report</h1>
|
|
149
|
+
<div class="meta">
|
|
150
|
+
<p><strong>Date:</strong> ${new Date(result.date).toLocaleString()}</p>
|
|
151
|
+
<p><strong>Files Analyzed:</strong> ${result.fileCount}</p>
|
|
152
|
+
<p><strong>Directory:</strong> ${result.options.directory}</p>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
<div class="section">
|
|
157
|
+
<h2>š File Name Case Analysis</h2>
|
|
158
|
+
<table>
|
|
159
|
+
<thead>
|
|
160
|
+
<tr>
|
|
161
|
+
<th>Case Type</th>
|
|
162
|
+
<th>Count</th>
|
|
163
|
+
<th>Percentage</th>
|
|
164
|
+
</tr>
|
|
165
|
+
</thead>
|
|
166
|
+
<tbody>
|
|
167
|
+
${Object.entries(result.fileNameCases)
|
|
168
|
+
.sort(([, a], [, b]) => b - a)
|
|
169
|
+
.map(([key, value]) => `
|
|
170
|
+
<tr>
|
|
171
|
+
<td>${key}</td>
|
|
172
|
+
<td>${value}</td>
|
|
173
|
+
<td class="percentage">${((value / result.fileCount) *
|
|
174
|
+
100).toFixed(2)}%</td>
|
|
175
|
+
</tr>
|
|
176
|
+
`)
|
|
177
|
+
.join("")}
|
|
178
|
+
</tbody>
|
|
179
|
+
</table>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<div class="section">
|
|
183
|
+
<h2>š Content Type Analysis</h2>
|
|
184
|
+
<table>
|
|
185
|
+
<thead>
|
|
186
|
+
<tr>
|
|
187
|
+
<th>Type</th>
|
|
188
|
+
<th>Count</th>
|
|
189
|
+
<th>Percentage</th>
|
|
190
|
+
</tr>
|
|
191
|
+
</thead>
|
|
192
|
+
<tbody>
|
|
193
|
+
${Object.entries(result.output)
|
|
194
|
+
.map(([key, value]) => `
|
|
195
|
+
<tr>
|
|
196
|
+
<td>${key}</td>
|
|
197
|
+
<td>${value}</td>
|
|
198
|
+
<td class="percentage">${((value / result.output.Physical) *
|
|
199
|
+
100).toFixed(2)}%</td>
|
|
200
|
+
</tr>
|
|
201
|
+
`)
|
|
202
|
+
.join("")}
|
|
203
|
+
</tbody>
|
|
204
|
+
</table>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div class="section">
|
|
208
|
+
<h2>āļø Analysis Options</h2>
|
|
209
|
+
<p><strong>Framework:</strong> ${result.options.framework || "N/A"}</p>
|
|
210
|
+
<p><strong>Extensions:</strong> ${((_a = result.options.extensions) === null || _a === void 0 ? void 0 : _a.join(", ")) || "All"}</p>
|
|
211
|
+
<p><strong>Exclude:</strong> ${((_b = result.options.exclude) === null || _b === void 0 ? void 0 : _b.join(", ")) || "None"}</p>
|
|
212
|
+
</div>
|
|
213
|
+
</body>
|
|
214
|
+
</html>
|
|
215
|
+
`.trim();
|
|
216
|
+
await fs.writeFile(outputPath, html, "utf-8");
|
|
217
|
+
return outputPath;
|
|
218
|
+
};
|
package/dist/utils/output.js
CHANGED
|
@@ -6,8 +6,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.logOutput = void 0;
|
|
7
7
|
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const boxen_1 = __importDefault(require("boxen"));
|
|
9
10
|
const file_1 = require("./file");
|
|
10
|
-
const
|
|
11
|
+
const export_1 = require("./export");
|
|
12
|
+
const getEmoji = (key) => {
|
|
13
|
+
const emojiMap = {
|
|
14
|
+
Physical: "š",
|
|
15
|
+
Source: "š»",
|
|
16
|
+
Comment: "š¬",
|
|
17
|
+
SingleLineComment: "//",
|
|
18
|
+
BlockComment: "/* */",
|
|
19
|
+
Mixed: "š",
|
|
20
|
+
EmptyBlockComment: "āŖ",
|
|
21
|
+
Empty: "ā¬",
|
|
22
|
+
ToDo: "ā
",
|
|
23
|
+
};
|
|
24
|
+
return emojiMap[key] || "š";
|
|
25
|
+
};
|
|
26
|
+
const formatPercentage = (value, total) => {
|
|
27
|
+
const percentage = ((value / total) * 100).toFixed(2);
|
|
28
|
+
const percentageNum = parseFloat(percentage);
|
|
29
|
+
const color = percentageNum > 50
|
|
30
|
+
? chalk_1.default.green
|
|
31
|
+
: percentageNum > 25
|
|
32
|
+
? chalk_1.default.yellow
|
|
33
|
+
: chalk_1.default.gray;
|
|
34
|
+
return color(`${percentage}%`);
|
|
35
|
+
};
|
|
36
|
+
const logOutput = async ({ fileCount, output, fileNameCases, options, duration, exportFormat, exportPath, }) => {
|
|
37
|
+
var _a;
|
|
11
38
|
const resultObject = {
|
|
12
39
|
date: new Date().toISOString(),
|
|
13
40
|
fileCount,
|
|
@@ -15,52 +42,121 @@ const logOutput = async ({ fileCount, output, fileNameCases, options, }) => {
|
|
|
15
42
|
options,
|
|
16
43
|
output,
|
|
17
44
|
};
|
|
18
|
-
//
|
|
19
|
-
|
|
45
|
+
// Enhanced header with box
|
|
46
|
+
const header = (0, boxen_1.default)(chalk_1.default.bold.cyan("š Codebase Analysis Results"), {
|
|
47
|
+
padding: 1,
|
|
48
|
+
borderColor: "cyan",
|
|
49
|
+
borderStyle: "round",
|
|
50
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
51
|
+
});
|
|
52
|
+
console.log(header);
|
|
20
53
|
if (fileCount === 0) {
|
|
21
|
-
|
|
54
|
+
const errorBox = (0, boxen_1.default)(chalk_1.default.red(`ā No files found in ${chalk_1.default.cyan(options.directory)}\n\nExtensions: ${chalk_1.default.cyan(((_a = options.extensions) === null || _a === void 0 ? void 0 : _a.join(", ")) || "all")}`), {
|
|
55
|
+
padding: 1,
|
|
56
|
+
borderColor: "red",
|
|
57
|
+
borderStyle: "round",
|
|
58
|
+
});
|
|
59
|
+
console.log(errorBox);
|
|
60
|
+
return;
|
|
22
61
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
head: [
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
// File count summary
|
|
63
|
+
const summaryBox = (0, boxen_1.default)(chalk_1.default.bold(`š Files Analyzed: ${chalk_1.default.yellow(fileCount)}`) +
|
|
64
|
+
(duration
|
|
65
|
+
? chalk_1.default.gray(`\nā±ļø Duration: ${chalk_1.default.yellow(duration.toFixed(2))}s`)
|
|
66
|
+
: ""), {
|
|
67
|
+
padding: 1,
|
|
68
|
+
borderColor: "blue",
|
|
69
|
+
borderStyle: "round",
|
|
70
|
+
margin: { top: 0, bottom: 1, left: 0, right: 0 },
|
|
71
|
+
});
|
|
72
|
+
console.log(summaryBox);
|
|
73
|
+
if (options.checkFileNames) {
|
|
74
|
+
console.log(chalk_1.default.bold.cyan("\nš File Name Case Analysis\n"));
|
|
75
|
+
const fileNameTable = new cli_table3_1.default({
|
|
76
|
+
head: [
|
|
77
|
+
chalk_1.default.bold.white("š Case Type"),
|
|
78
|
+
chalk_1.default.bold.white("Count"),
|
|
79
|
+
chalk_1.default.bold.white("Percentage"),
|
|
80
|
+
],
|
|
81
|
+
colWidths: [30, 15, 20],
|
|
82
|
+
style: {
|
|
83
|
+
head: ["cyan"],
|
|
84
|
+
border: ["gray"],
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
// Sort by count descending
|
|
88
|
+
const sortedCases = Object.entries(fileNameCases).sort(([, a], [, b]) => b - a);
|
|
89
|
+
sortedCases.forEach(([key, value]) => {
|
|
90
|
+
const percentage = ((value / fileCount) * 100).toFixed(2);
|
|
91
|
+
fileNameTable.push([
|
|
92
|
+
chalk_1.default.cyan(key),
|
|
93
|
+
chalk_1.default.yellow(value.toString()),
|
|
94
|
+
formatPercentage(value, fileCount),
|
|
95
|
+
]);
|
|
96
|
+
});
|
|
97
|
+
console.log(fileNameTable.toString());
|
|
98
|
+
console.log();
|
|
99
|
+
}
|
|
100
|
+
if (options.checkFileContent) {
|
|
101
|
+
console.log(chalk_1.default.bold.cyan("\nš Content Type Analysis\n"));
|
|
102
|
+
const contentTypeTable = new cli_table3_1.default({
|
|
103
|
+
head: [
|
|
104
|
+
chalk_1.default.bold.white("Type"),
|
|
105
|
+
chalk_1.default.bold.white("Count"),
|
|
106
|
+
chalk_1.default.bold.white("Percentage"),
|
|
107
|
+
],
|
|
108
|
+
colWidths: [30, 15, 20],
|
|
109
|
+
style: {
|
|
110
|
+
head: ["cyan"],
|
|
111
|
+
border: ["gray"],
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
Object.entries(output).forEach(([key, value]) => {
|
|
115
|
+
const emoji = getEmoji(key);
|
|
116
|
+
const percentage = ((value / output.Physical) * 100).toFixed(2);
|
|
117
|
+
contentTypeTable.push([
|
|
118
|
+
`${emoji} ${chalk_1.default.cyan(key)}`,
|
|
119
|
+
chalk_1.default.yellow(value.toString()),
|
|
120
|
+
formatPercentage(value, output.Physical),
|
|
121
|
+
]);
|
|
122
|
+
});
|
|
123
|
+
console.log(contentTypeTable.toString());
|
|
124
|
+
console.log();
|
|
57
125
|
}
|
|
58
126
|
// Write the JSON output to a file (optional)
|
|
59
127
|
if (options.writeJsonOutput) {
|
|
60
128
|
const writePath = await (0, file_1.writeAnalyzeResult)(options.directory, resultObject);
|
|
61
|
-
|
|
129
|
+
const successBox = (0, boxen_1.default)(chalk_1.default.green(`ā
JSON output written to:\n${chalk_1.default.cyan(writePath)}`), {
|
|
130
|
+
padding: 1,
|
|
131
|
+
borderColor: "green",
|
|
132
|
+
borderStyle: "round",
|
|
133
|
+
margin: { top: 1, bottom: 0, left: 0, right: 0 },
|
|
134
|
+
});
|
|
135
|
+
console.log(successBox);
|
|
136
|
+
}
|
|
137
|
+
// Export in specified format
|
|
138
|
+
if (exportFormat && exportPath) {
|
|
139
|
+
try {
|
|
140
|
+
const exportedPath = await (0, export_1.exportResults)(resultObject, exportPath, exportFormat);
|
|
141
|
+
const exportBox = (0, boxen_1.default)(chalk_1.default.green(`š¤ Exported ${exportFormat.toUpperCase()} report to:\n${chalk_1.default.cyan(exportedPath)}`), {
|
|
142
|
+
padding: 1,
|
|
143
|
+
borderColor: "green",
|
|
144
|
+
borderStyle: "round",
|
|
145
|
+
margin: { top: 1, bottom: 0, left: 0, right: 0 },
|
|
146
|
+
});
|
|
147
|
+
console.log(exportBox);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
console.error(chalk_1.default.red(`\nā Error exporting ${exportFormat}: ${error}\n`));
|
|
151
|
+
}
|
|
62
152
|
}
|
|
63
|
-
|
|
64
|
-
|
|
153
|
+
// Footer
|
|
154
|
+
const footer = (0, boxen_1.default)(chalk_1.default.gray(`Analysis completed at ${new Date().toLocaleTimeString()}`), {
|
|
155
|
+
padding: { top: 0, right: 1, bottom: 0, left: 1 },
|
|
156
|
+
borderColor: "gray",
|
|
157
|
+
borderStyle: "round",
|
|
158
|
+
margin: { top: 1, bottom: 0, left: 0, right: 0 },
|
|
159
|
+
});
|
|
160
|
+
console.log(footer);
|
|
65
161
|
};
|
|
66
162
|
exports.logOutput = logOutput;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ProgressBar = void 0;
|
|
7
|
+
const cli_progress_1 = __importDefault(require("cli-progress"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
class ProgressBar {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.bar = null;
|
|
12
|
+
this.current = 0;
|
|
13
|
+
this.total = options.total;
|
|
14
|
+
const format = options.format ||
|
|
15
|
+
`${chalk_1.default.cyan("{bar}")} | {percentage}% | {value}/{total} files`;
|
|
16
|
+
this.bar = new cli_progress_1.default.SingleBar({
|
|
17
|
+
format,
|
|
18
|
+
barCompleteChar: "\u2588",
|
|
19
|
+
barIncompleteChar: "\u2591",
|
|
20
|
+
hideCursor: true,
|
|
21
|
+
clearOnComplete: true,
|
|
22
|
+
}, cli_progress_1.default.Presets.shades_classic);
|
|
23
|
+
this.bar.start(this.total, 0);
|
|
24
|
+
}
|
|
25
|
+
update(value = 1, payload) {
|
|
26
|
+
this.current += value;
|
|
27
|
+
if (this.bar) {
|
|
28
|
+
this.bar.update(this.current, payload);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
setTotal(total) {
|
|
32
|
+
this.total = total;
|
|
33
|
+
if (this.bar) {
|
|
34
|
+
this.bar.setTotal(total);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
stop() {
|
|
38
|
+
if (this.bar) {
|
|
39
|
+
this.bar.stop();
|
|
40
|
+
this.bar = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
complete() {
|
|
44
|
+
if (this.bar) {
|
|
45
|
+
this.bar.update(this.total);
|
|
46
|
+
this.bar.stop();
|
|
47
|
+
this.bar = null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.ProgressBar = ProgressBar;
|