qleaner 1.2.0 → 1.3.0
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 +177 -67
- package/command.js +137 -202
- package/controllers/image.js +146 -508
- package/controllers/initialize.js +4 -2
- package/controllers/list.js +21 -7
- package/controllers/summary.js +176 -143
- package/package.json +2 -4
- package/qleaner.config.json +26 -2
- package/test.js +2 -0
- package/utils/astParser.js +224 -0
- package/utils/cache.js +15 -0
- package/utils/constants.js +29 -3
- package/utils/cssImages.js +130 -51
- package/utils/fileProcessing.js +184 -0
- package/utils/graphComparison.js +144 -0
- package/utils/graphOperations.js +141 -0
- package/utils/graphUtils.js +32 -0
- package/utils/imageAstParser.js +220 -0
- package/utils/imageDisplay.js +113 -0
- package/utils/imageExtractors.js +82 -0
- package/utils/imageGraphUtils.js +96 -0
- package/utils/imagePathBuilder.js +50 -0
- package/utils/pathBuilder.js +33 -0
- package/utils/resolver.js +58 -7
- package/utils/summary.js +32 -7
- package/utils/utils.js +139 -89
|
@@ -11,13 +11,15 @@ const {
|
|
|
11
11
|
|
|
12
12
|
async function init(chalk) {
|
|
13
13
|
const responses = await prompts(QUESTIONS);
|
|
14
|
-
const ISROOT = responses.alias ?
|
|
15
|
-
const ALIAS = responses.alias ? true : false;
|
|
14
|
+
const ISROOT = responses.alias === "relative" ? true : false;
|
|
15
|
+
const ALIAS = responses.alias === "public" ? true : false;
|
|
16
16
|
const configFile = path.join(process.cwd(), "qleaner.config.json");
|
|
17
17
|
fs.writeFileSync(
|
|
18
18
|
configFile,
|
|
19
19
|
JSON.stringify(
|
|
20
20
|
{
|
|
21
|
+
codeAlias: responses.codeAlias,
|
|
22
|
+
paths: {},
|
|
21
23
|
packageManager: responses.packageManager || "npm",
|
|
22
24
|
excludeDir: DIRECTORIES_TO_EXCLUDE, // Exclude directories from the scan
|
|
23
25
|
excludeFile: ["payload-types.ts", "payload-types.js"], // Exclude files from the scan
|
package/controllers/list.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
const { unUsedFiles
|
|
1
|
+
const { unUsedFiles } = require("../command");
|
|
2
|
+
const { hydrateGraph } = require("../utils/graphUtils");
|
|
2
3
|
const Table = require('cli-table3');
|
|
3
|
-
const { askDeleteFiles } = require("../utils/utils");
|
|
4
|
+
const { askDeleteFiles, loadTSConfig } = require("../utils/utils");
|
|
4
5
|
const fs = require('fs');
|
|
5
6
|
const path = require('path');
|
|
6
7
|
const { summarizeAll, getTop10LargestFiles, dependenciesSummary, formatFilePath } = require("./summary");
|
|
@@ -17,7 +18,7 @@ const { query, unusedDependencies: findUnusedDeps } = require("./query");
|
|
|
17
18
|
let cache;
|
|
18
19
|
try {
|
|
19
20
|
cache = JSON.parse(fs.readFileSync(cachePath, "utf8"));
|
|
20
|
-
} catch
|
|
21
|
+
} catch {
|
|
21
22
|
console.log(chalk.red('⚠️ Error reading cache file. Please run "qleaner scan" again.'));
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
@@ -77,7 +78,7 @@ async function unusedDependencies(chalk, directoryPath = process.cwd(), options
|
|
|
77
78
|
let packageJson;
|
|
78
79
|
try {
|
|
79
80
|
packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
80
|
-
} catch
|
|
81
|
+
} catch {
|
|
81
82
|
console.log(chalk.red('⚠️ Error reading package.json file.'));
|
|
82
83
|
return;
|
|
83
84
|
}
|
|
@@ -99,7 +100,7 @@ async function unusedDependencies(chalk, directoryPath = process.cwd(), options
|
|
|
99
100
|
let cache;
|
|
100
101
|
try {
|
|
101
102
|
cache = JSON.parse(fs.readFileSync(cachePath, "utf8"));
|
|
102
|
-
} catch
|
|
103
|
+
} catch {
|
|
103
104
|
console.log(chalk.red('⚠️ Error reading cache file. Please run "qleaner scan" again.'));
|
|
104
105
|
return;
|
|
105
106
|
}
|
|
@@ -155,7 +156,9 @@ async function summary(chalk, options) {
|
|
|
155
156
|
}
|
|
156
157
|
}
|
|
157
158
|
|
|
158
|
-
|
|
159
|
+
|
|
160
|
+
async function scan(ora, chalk, pathToScan, options) {
|
|
161
|
+
let pathConfig = {};
|
|
159
162
|
// check if qleaner.config.json exists
|
|
160
163
|
if (fs.existsSync(path.join(process.cwd(), "qleaner.config.json"))) {
|
|
161
164
|
const config = JSON.parse(
|
|
@@ -165,9 +168,20 @@ async function scan(ora, chalk, filePath, options) {
|
|
|
165
168
|
...config,
|
|
166
169
|
...options,
|
|
167
170
|
};
|
|
171
|
+
// config file will take precedence over the tsconfig.json file
|
|
172
|
+
if(config.paths && Object.entries(config.paths).length > 0){
|
|
173
|
+
pathConfig = config.paths;
|
|
174
|
+
}else if(config.codeAlias){
|
|
175
|
+
pathConfig = loadTSConfig(pathToScan, config.codeAlias).paths;
|
|
176
|
+
if(!pathConfig){
|
|
177
|
+
console.log(chalk.red('⚠️ Error reading config file. Please run "qleaner scan" again.'));
|
|
178
|
+
pathConfig = {}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
168
181
|
}
|
|
182
|
+
|
|
169
183
|
// read qleaner.config.json
|
|
170
|
-
const unusedFiles = await unUsedFiles(ora, chalk,
|
|
184
|
+
const unusedFiles = await unUsedFiles(ora, chalk,pathToScan, pathConfig, options);
|
|
171
185
|
|
|
172
186
|
let totalSize = 0;
|
|
173
187
|
const fileArray = Array.from(unusedFiles.values());
|
package/controllers/summary.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const Table = require("cli-table3");
|
|
4
|
-
const { hydrateGraph } = require("../
|
|
4
|
+
const { hydrateGraph } = require("../utils/graphUtils");
|
|
5
5
|
const {
|
|
6
6
|
getDeadLinks,
|
|
7
7
|
getTotalImageSize,
|
|
@@ -13,8 +13,9 @@ const {
|
|
|
13
13
|
getTop10FilesWithLightDependencies,
|
|
14
14
|
getTop10FilesHotspots,
|
|
15
15
|
getTotalImageFiles,
|
|
16
|
-
getTop10FilesCodeDependencies,
|
|
17
16
|
getTop10FilesDependenciesHotspots,
|
|
17
|
+
getTop10FilesWithMostReexports,
|
|
18
|
+
getTop10FilesWithMostReexportedBy,
|
|
18
19
|
} = require("../utils/summary");
|
|
19
20
|
|
|
20
21
|
function formatFilePath(filePath, maxLength = 70) {
|
|
@@ -29,7 +30,7 @@ function formatFilePath(filePath, maxLength = 70) {
|
|
|
29
30
|
relativePath = filePath; // Use absolute if outside project
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
|
-
} catch
|
|
33
|
+
} catch {
|
|
33
34
|
// If conversion fails, use original path
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -196,7 +197,7 @@ function getTop10LargestFiles(chalk) {
|
|
|
196
197
|
colWidths: [90, 15],
|
|
197
198
|
style: { head: [], border: [] },
|
|
198
199
|
});
|
|
199
|
-
top10CodeFiles.forEach((file
|
|
200
|
+
top10CodeFiles.forEach((file) => {
|
|
200
201
|
codeTable.push([
|
|
201
202
|
chalk.white(formatFilePath(file[0], 85)),
|
|
202
203
|
chalk.magenta((file[1] / 1024).toFixed(2) + " KB"),
|
|
@@ -279,10 +280,146 @@ function getTop10LargestFiles(chalk) {
|
|
|
279
280
|
);
|
|
280
281
|
}
|
|
281
282
|
|
|
282
|
-
function
|
|
283
|
-
|
|
283
|
+
function printSectionHeader(chalk, title) {
|
|
284
|
+
console.log(chalk.green.bold(`\n${title}`));
|
|
285
|
+
console.log(chalk.green("════════════════════════════════════════════════"));
|
|
286
|
+
}
|
|
284
287
|
|
|
285
|
-
|
|
288
|
+
function printSectionFooter(chalk) {
|
|
289
|
+
console.log(chalk.green("════════════════════════════════════════════════"));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function displayTableSection(chalk, data, config) {
|
|
293
|
+
const { title, emptyMessage, headers, colWidths, getRowData, emptyMessageColor } = config;
|
|
294
|
+
|
|
295
|
+
printSectionHeader(chalk, title);
|
|
296
|
+
|
|
297
|
+
if (!data || data.length === 0) {
|
|
298
|
+
const colorFn = emptyMessageColor || chalk.yellow;
|
|
299
|
+
console.log(colorFn(` ${emptyMessage}`));
|
|
300
|
+
} else {
|
|
301
|
+
const table = new Table({
|
|
302
|
+
head: headers.map(h => chalk.cyan(h)),
|
|
303
|
+
colWidths,
|
|
304
|
+
style: { head: [], border: [] },
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
data.forEach((item) => {
|
|
308
|
+
table.push(getRowData(item));
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
console.log(table.toString());
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
printSectionFooter(chalk);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function displayHeavyDependencies(chalk, data) {
|
|
318
|
+
displayTableSection(chalk, data, {
|
|
319
|
+
title: "🔴 Top 10 Files with Heavy Dependencies",
|
|
320
|
+
emptyMessage: "No files with heavy dependencies found.",
|
|
321
|
+
headers: ["File Path", "Imports"],
|
|
322
|
+
colWidths: [90, 12],
|
|
323
|
+
getRowData: ([filePath, node]) => [
|
|
324
|
+
chalk.white(formatFilePath(filePath, 85)),
|
|
325
|
+
chalk.red((node.imports?.size || 0).toString()),
|
|
326
|
+
],
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function displayLightDependencies(chalk, data) {
|
|
331
|
+
displayTableSection(chalk, data, {
|
|
332
|
+
title: "🟢 Top 10 Files with Light Dependencies",
|
|
333
|
+
emptyMessage: "No files with light dependencies found.",
|
|
334
|
+
headers: ["File Path", "Imports"],
|
|
335
|
+
colWidths: [90, 12],
|
|
336
|
+
getRowData: ([filePath, node]) => [
|
|
337
|
+
chalk.white(formatFilePath(filePath, 85)),
|
|
338
|
+
chalk.green((node.imports?.size || 0).toString()),
|
|
339
|
+
],
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function displayFileHotspots(chalk, data) {
|
|
344
|
+
displayTableSection(chalk, data, {
|
|
345
|
+
title: "🔥 Top 10 File Hotspots (Most Imported)",
|
|
346
|
+
emptyMessage: "No file hotspots found.",
|
|
347
|
+
headers: ["File Path", "Imported By"],
|
|
348
|
+
colWidths: [90, 15],
|
|
349
|
+
getRowData: ([filePath, node]) => [
|
|
350
|
+
chalk.white(formatFilePath(filePath, 85)),
|
|
351
|
+
chalk.magenta((node.importedBy?.size || 0).toString()),
|
|
352
|
+
],
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function displayDependencyHotspots(chalk, data) {
|
|
357
|
+
displayTableSection(chalk, data, {
|
|
358
|
+
title: "📦 Top 10 Files Dependencies Hotspots",
|
|
359
|
+
emptyMessage: "No dependency hotspots found.",
|
|
360
|
+
headers: ["File Path", "Dependencies"],
|
|
361
|
+
colWidths: [90, 15],
|
|
362
|
+
getRowData: ([filePath, node]) => [
|
|
363
|
+
chalk.white(formatFilePath(filePath, 85)),
|
|
364
|
+
chalk.yellow((node.importedBy?.size || 0).toString()),
|
|
365
|
+
],
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function displayReexports(chalk, data) {
|
|
370
|
+
displayTableSection(chalk, data, {
|
|
371
|
+
title: "📤 Top 10 Files that Export the Most Files",
|
|
372
|
+
emptyMessage: "No files with re-exports found.",
|
|
373
|
+
headers: ["File Path", "Re-exports"],
|
|
374
|
+
colWidths: [90, 15],
|
|
375
|
+
getRowData: ([filePath, node]) => [
|
|
376
|
+
chalk.white(formatFilePath(filePath, 85)),
|
|
377
|
+
chalk.blue((node.reExported?.size || 0).toString()),
|
|
378
|
+
],
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function displayReexportedBy(chalk, data) {
|
|
383
|
+
displayTableSection(chalk, data, {
|
|
384
|
+
title: "📥 Top 10 Files that Have Been Exported Most",
|
|
385
|
+
emptyMessage: "No files re-exported by others found.",
|
|
386
|
+
headers: ["File Path", "Re-exported By"],
|
|
387
|
+
colWidths: [90, 15],
|
|
388
|
+
getRowData: ([filePath, node]) => [
|
|
389
|
+
chalk.white(formatFilePath(filePath, 85)),
|
|
390
|
+
chalk.cyan((node.reExportedBy?.size || 0).toString()),
|
|
391
|
+
],
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function displayDeadImageHotspots(chalk, data) {
|
|
396
|
+
displayTableSection(chalk, data, {
|
|
397
|
+
title: "🖼️ Top 10 Dead Image Hotspots",
|
|
398
|
+
emptyMessage: "✓ No dead image hotspots found.",
|
|
399
|
+
headers: ["File Path", "Referenced By"],
|
|
400
|
+
colWidths: [90, 15],
|
|
401
|
+
getRowData: ([filePath, node]) => [
|
|
402
|
+
chalk.white(formatFilePath(filePath, 85)),
|
|
403
|
+
chalk.red((node.importedBy?.size || 0).toString()),
|
|
404
|
+
],
|
|
405
|
+
emptyMessageColor: chalk.green,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function displayAliveImageHotspots(chalk, data) {
|
|
410
|
+
displayTableSection(chalk, data, {
|
|
411
|
+
title: "✅ Top 10 Alive Image Hotspots",
|
|
412
|
+
emptyMessage: "No alive image hotspots found.",
|
|
413
|
+
headers: ["File Path", "Referenced By"],
|
|
414
|
+
colWidths: [90, 15],
|
|
415
|
+
getRowData: ([filePath, node]) => [
|
|
416
|
+
chalk.white(formatFilePath(filePath, 85)),
|
|
417
|
+
chalk.green((node.importedBy?.size || 0).toString()),
|
|
418
|
+
],
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function checkDataAvailability(chalk, codeGraph, imageGraph) {
|
|
286
423
|
const hasData = codeGraph.graph.size > 0 || imageGraph.graph.size > 0;
|
|
287
424
|
if (!hasData) {
|
|
288
425
|
console.log(chalk.yellow.bold("\n⚠️ No Project Data Found"));
|
|
@@ -293,9 +430,12 @@ function dependenciesSummary(chalk) {
|
|
|
293
430
|
console.log(
|
|
294
431
|
chalk.yellow(" Use the scan command to generate project data.\n")
|
|
295
432
|
);
|
|
296
|
-
return;
|
|
433
|
+
return false;
|
|
297
434
|
}
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
298
437
|
|
|
438
|
+
function collectDependencyData(codeGraph, imageGraph) {
|
|
299
439
|
const top10FilesWithHeavyDependencies = getTop10FilesWithHeavyDependencies(
|
|
300
440
|
codeGraph.graph
|
|
301
441
|
);
|
|
@@ -304,153 +444,46 @@ function dependenciesSummary(chalk) {
|
|
|
304
444
|
);
|
|
305
445
|
const top10FilesHotspots = getTop10FilesDependenciesHotspots(codeGraph.graph, false);
|
|
306
446
|
const top10FilesDependenciesHotspots = getTop10FilesDependenciesHotspots(codeGraph.graph);
|
|
307
|
-
|
|
447
|
+
const top10FilesWithMostReexports = getTop10FilesWithMostReexports(codeGraph.graph);
|
|
448
|
+
const top10FilesWithMostReexportedBy = getTop10FilesWithMostReexportedBy(codeGraph.graph);
|
|
449
|
+
|
|
308
450
|
const { deadLinks, aliveLinks } = getDeadLinks(imageGraph.graph);
|
|
309
451
|
const top10FilesHotspotsDeadImage = getTop10FilesHotspots(deadLinks);
|
|
310
452
|
const top10FilesHotspotsAliveImage = getTop10FilesHotspots(aliveLinks, false);
|
|
311
453
|
|
|
312
|
-
|
|
313
|
-
|
|
454
|
+
return {
|
|
455
|
+
top10FilesWithHeavyDependencies,
|
|
456
|
+
top10FilesWithLightDependencies,
|
|
457
|
+
top10FilesHotspots,
|
|
458
|
+
top10FilesDependenciesHotspots,
|
|
459
|
+
top10FilesWithMostReexports,
|
|
460
|
+
top10FilesWithMostReexportedBy,
|
|
461
|
+
top10FilesHotspotsDeadImage,
|
|
462
|
+
top10FilesHotspotsAliveImage,
|
|
463
|
+
};
|
|
464
|
+
}
|
|
314
465
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
console.log(chalk.green("════════════════════════════════════════════════"));
|
|
318
|
-
if (
|
|
319
|
-
!top10FilesWithHeavyDependencies ||
|
|
320
|
-
top10FilesWithHeavyDependencies.length === 0
|
|
321
|
-
) {
|
|
322
|
-
console.log(chalk.yellow(" No files with heavy dependencies found."));
|
|
323
|
-
} else {
|
|
324
|
-
const heavyDepsTable = new Table({
|
|
325
|
-
head: [chalk.cyan("File Path"), chalk.cyan("Imports")],
|
|
326
|
-
colWidths: [90, 12],
|
|
327
|
-
style: { head: [], border: [] },
|
|
328
|
-
});
|
|
329
|
-
top10FilesWithHeavyDependencies.forEach(([filePath, node]) => {
|
|
330
|
-
heavyDepsTable.push([
|
|
331
|
-
chalk.white(formatFilePath(filePath, 85)),
|
|
332
|
-
chalk.red((node.imports?.size || 0).toString()),
|
|
333
|
-
]);
|
|
334
|
-
});
|
|
335
|
-
console.log(heavyDepsTable.toString());
|
|
336
|
-
}
|
|
337
|
-
console.log(chalk.green("════════════════════════════════════════════════"));
|
|
466
|
+
function dependenciesSummary(chalk) {
|
|
467
|
+
const { codeGraph, imageGraph } = readCacheAndHydrateGraph();
|
|
338
468
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
console.log(chalk.green("════════════════════════════════════════════════"));
|
|
342
|
-
if (
|
|
343
|
-
!top10FilesWithLightDependencies ||
|
|
344
|
-
top10FilesWithLightDependencies.length === 0
|
|
345
|
-
) {
|
|
346
|
-
console.log(chalk.yellow(" No files with light dependencies found."));
|
|
347
|
-
} else {
|
|
348
|
-
const lightDepsTable = new Table({
|
|
349
|
-
head: [chalk.cyan("File Path"), chalk.cyan("Imports")],
|
|
350
|
-
colWidths: [90, 12],
|
|
351
|
-
style: { head: [], border: [] },
|
|
352
|
-
});
|
|
353
|
-
top10FilesWithLightDependencies.forEach(([filePath, node]) => {
|
|
354
|
-
lightDepsTable.push([
|
|
355
|
-
chalk.white(formatFilePath(filePath, 85)),
|
|
356
|
-
chalk.green((node.imports?.size || 0).toString()),
|
|
357
|
-
]);
|
|
358
|
-
});
|
|
359
|
-
console.log(lightDepsTable.toString());
|
|
469
|
+
if (!checkDataAvailability(chalk, codeGraph, imageGraph)) {
|
|
470
|
+
return;
|
|
360
471
|
}
|
|
361
|
-
console.log(chalk.green("════════════════════════════════════════════════"));
|
|
362
472
|
|
|
363
|
-
|
|
364
|
-
console.log(chalk.green.bold("\n🔥 Top 10 File Hotspots (Most Imported)"));
|
|
365
|
-
console.log(chalk.green("════════════════════════════════════════════════"));
|
|
366
|
-
if (!top10FilesHotspots || top10FilesHotspots.length === 0) {
|
|
367
|
-
console.log(chalk.yellow(" No file hotspots found."));
|
|
368
|
-
} else {
|
|
369
|
-
const hotspotsTable = new Table({
|
|
370
|
-
head: [chalk.cyan("File Path"), chalk.cyan("Imported By")],
|
|
371
|
-
colWidths: [90, 15],
|
|
372
|
-
style: { head: [], border: [] },
|
|
373
|
-
});
|
|
374
|
-
top10FilesHotspots.forEach(([filePath, node]) => {
|
|
375
|
-
hotspotsTable.push([
|
|
376
|
-
chalk.white(formatFilePath(filePath, 85)),
|
|
377
|
-
chalk.magenta((node.importedBy?.size || 0).toString()),
|
|
378
|
-
]);
|
|
379
|
-
});
|
|
380
|
-
console.log(hotspotsTable.toString());
|
|
381
|
-
}
|
|
382
|
-
console.log(chalk.green("════════════════════════════════════════════════"));
|
|
473
|
+
const data = collectDependencyData(codeGraph, imageGraph);
|
|
383
474
|
|
|
384
|
-
|
|
385
|
-
console.log(chalk.green.bold("\n📦 Top 10 Files Dependencies Hotspots"));
|
|
386
|
-
console.log(chalk.green("════════════════════════════════════════════════"));
|
|
387
|
-
if (
|
|
388
|
-
!top10FilesDependenciesHotspots ||
|
|
389
|
-
top10FilesDependenciesHotspots.length === 0
|
|
390
|
-
) {
|
|
391
|
-
console.log(chalk.yellow(" No dependency hotspots found."));
|
|
392
|
-
} else {
|
|
393
|
-
const dependenciesHotspotsTable = new Table({
|
|
394
|
-
head: [chalk.cyan("File Path"), chalk.cyan("Dependencies")],
|
|
395
|
-
colWidths: [90, 15],
|
|
396
|
-
style: { head: [], border: [] },
|
|
397
|
-
});
|
|
398
|
-
top10FilesDependenciesHotspots.forEach(([filePath, node]) => {
|
|
399
|
-
dependenciesHotspotsTable.push([
|
|
400
|
-
chalk.white(formatFilePath(filePath, 85)),
|
|
401
|
-
chalk.yellow((node.importedBy?.size || 0).toString()),
|
|
402
|
-
]);
|
|
403
|
-
});
|
|
404
|
-
console.log(dependenciesHotspotsTable.toString());
|
|
405
|
-
}
|
|
475
|
+
console.log(chalk.green.bold("\n📊 Dependencies Summary"));
|
|
406
476
|
console.log(chalk.green("════════════════════════════════════════════════"));
|
|
407
477
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
)
|
|
415
|
-
|
|
416
|
-
} else {
|
|
417
|
-
const deadImageHotspotsTable = new Table({
|
|
418
|
-
head: [chalk.cyan("File Path"), chalk.cyan("Referenced By")],
|
|
419
|
-
colWidths: [90, 15],
|
|
420
|
-
style: { head: [], border: [] },
|
|
421
|
-
});
|
|
422
|
-
top10FilesHotspotsDeadImage.forEach(([filePath, node]) => {
|
|
423
|
-
deadImageHotspotsTable.push([
|
|
424
|
-
chalk.white(formatFilePath(filePath, 85)),
|
|
425
|
-
chalk.red((node.importedBy?.size || 0).toString()),
|
|
426
|
-
]);
|
|
427
|
-
});
|
|
428
|
-
console.log(deadImageHotspotsTable.toString());
|
|
429
|
-
}
|
|
430
|
-
console.log(chalk.green("════════════════════════════════════════════════"));
|
|
478
|
+
displayHeavyDependencies(chalk, data.top10FilesWithHeavyDependencies);
|
|
479
|
+
displayLightDependencies(chalk, data.top10FilesWithLightDependencies);
|
|
480
|
+
displayFileHotspots(chalk, data.top10FilesHotspots);
|
|
481
|
+
displayDependencyHotspots(chalk, data.top10FilesDependenciesHotspots);
|
|
482
|
+
displayReexports(chalk, data.top10FilesWithMostReexports);
|
|
483
|
+
displayReexportedBy(chalk, data.top10FilesWithMostReexportedBy);
|
|
484
|
+
displayDeadImageHotspots(chalk, data.top10FilesHotspotsDeadImage);
|
|
485
|
+
displayAliveImageHotspots(chalk, data.top10FilesHotspotsAliveImage);
|
|
431
486
|
|
|
432
|
-
// Image Hotspots - Alive Links
|
|
433
|
-
console.log(chalk.green.bold("\n✅ Top 10 Alive Image Hotspots"));
|
|
434
|
-
console.log(chalk.green("════════════════════════════════════════════════"));
|
|
435
|
-
if (
|
|
436
|
-
!top10FilesHotspotsAliveImage ||
|
|
437
|
-
top10FilesHotspotsAliveImage.length === 0
|
|
438
|
-
) {
|
|
439
|
-
console.log(chalk.yellow(" No alive image hotspots found."));
|
|
440
|
-
} else {
|
|
441
|
-
const aliveImageHotspotsTable = new Table({
|
|
442
|
-
head: [chalk.cyan("File Path"), chalk.cyan("Referenced By")],
|
|
443
|
-
colWidths: [90, 15],
|
|
444
|
-
style: { head: [], border: [] },
|
|
445
|
-
});
|
|
446
|
-
top10FilesHotspotsAliveImage.forEach(([filePath, node]) => {
|
|
447
|
-
aliveImageHotspotsTable.push([
|
|
448
|
-
chalk.white(formatFilePath(filePath, 85)),
|
|
449
|
-
chalk.green((node.importedBy?.size || 0).toString()),
|
|
450
|
-
]);
|
|
451
|
-
});
|
|
452
|
-
console.log(aliveImageHotspotsTable.toString());
|
|
453
|
-
}
|
|
454
487
|
console.log(
|
|
455
488
|
chalk.green("════════════════════════════════════════════════\n")
|
|
456
489
|
);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qleaner",
|
|
3
3
|
"description": "A CLI tool for analyzing and identifying unused files, images, imports, and dead code in React, TypeScript, and JavaScript projects to help optimize bundle size and maintain clean codebases",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.3.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "command.js",
|
|
7
7
|
"bin": "bin/cli.js",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"@types/node": "^20.10.5",
|
|
61
61
|
"eslint": "^9.39.2",
|
|
62
62
|
"globals": "^16.5.0",
|
|
63
|
-
"typescript": "^5.
|
|
63
|
+
"typescript": "^5.9.3"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@babel/parser": "^7.28.5",
|
|
@@ -75,5 +75,3 @@
|
|
|
75
75
|
"prompts": "^2.4.2"
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
|
|
79
|
-
|
package/qleaner.config.json
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
+
"codeAlias": null,
|
|
3
|
+
"paths": {},
|
|
2
4
|
"packageManager": "yarn",
|
|
3
5
|
"excludeDir": [
|
|
4
6
|
"node_modules",
|
|
@@ -29,7 +31,20 @@
|
|
|
29
31
|
"test.tsx",
|
|
30
32
|
"test.ts",
|
|
31
33
|
"test.js",
|
|
32
|
-
"test.jsx"
|
|
34
|
+
"test.jsx",
|
|
35
|
+
"spec.tsx",
|
|
36
|
+
"spec.ts",
|
|
37
|
+
"spec.js",
|
|
38
|
+
"spec.jsx",
|
|
39
|
+
"d.ts",
|
|
40
|
+
"config.js",
|
|
41
|
+
"config.cjs",
|
|
42
|
+
"config.mjs",
|
|
43
|
+
"config.ts",
|
|
44
|
+
"setupTests.ts",
|
|
45
|
+
"setupTests.js",
|
|
46
|
+
"setup.ts",
|
|
47
|
+
"setup.js"
|
|
33
48
|
],
|
|
34
49
|
"excludeFilePrint": [
|
|
35
50
|
"page.tsx",
|
|
@@ -53,7 +68,16 @@
|
|
|
53
68
|
"_document.tsx",
|
|
54
69
|
"_document.jsx",
|
|
55
70
|
"_error.tsx",
|
|
56
|
-
"_error.jsx"
|
|
71
|
+
"_error.jsx",
|
|
72
|
+
"robots.ts",
|
|
73
|
+
"robots.js",
|
|
74
|
+
"sitemap.ts",
|
|
75
|
+
"sitemap.js",
|
|
76
|
+
"manifest.ts",
|
|
77
|
+
"manifest.js",
|
|
78
|
+
"robots.txt",
|
|
79
|
+
"not-found.tsx",
|
|
80
|
+
"not-found.jsx"
|
|
57
81
|
],
|
|
58
82
|
"excludeDirAssets": [],
|
|
59
83
|
"excludeFileAssets": [],
|
package/test.js
ADDED