pruny 1.4.0 → 1.6.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/dist/index.js +137 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9394,17 +9394,54 @@ async function scanUnusedExports(config) {
|
|
|
9394
9394
|
for (const [file, exports] of exportMap.entries()) {
|
|
9395
9395
|
for (const exp of exports) {
|
|
9396
9396
|
let isUsed = false;
|
|
9397
|
+
let usedInternally = false;
|
|
9398
|
+
const fileContent = totalContents.get(file);
|
|
9399
|
+
if (fileContent) {
|
|
9400
|
+
const lines = fileContent.split(`
|
|
9401
|
+
`);
|
|
9402
|
+
for (let i = 0;i < lines.length; i++) {
|
|
9403
|
+
if (i === exp.line - 1)
|
|
9404
|
+
continue;
|
|
9405
|
+
const line = lines[i];
|
|
9406
|
+
if (isCommentOrString(line))
|
|
9407
|
+
continue;
|
|
9408
|
+
const cleanLine = stripStringsAndComments(line);
|
|
9409
|
+
const referenceRegex = new RegExp(`\\b${exp.name}\\b`);
|
|
9410
|
+
if (referenceRegex.test(cleanLine)) {
|
|
9411
|
+
usedInternally = true;
|
|
9412
|
+
break;
|
|
9413
|
+
}
|
|
9414
|
+
}
|
|
9415
|
+
}
|
|
9397
9416
|
for (const [otherFile, content] of totalContents.entries()) {
|
|
9398
9417
|
if (file === otherFile)
|
|
9399
9418
|
continue;
|
|
9400
|
-
const
|
|
9401
|
-
|
|
9419
|
+
const jsxPattern = new RegExp(`<${exp.name}[\\s/>]`);
|
|
9420
|
+
const importPattern = new RegExp(`import.*\\b${exp.name}\\b.*from`);
|
|
9421
|
+
const destructurePattern = new RegExp(`\\{[^}]*\\b${exp.name}\\b[^}]*\\}`);
|
|
9422
|
+
if (jsxPattern.test(content) || importPattern.test(content)) {
|
|
9402
9423
|
isUsed = true;
|
|
9403
9424
|
break;
|
|
9404
9425
|
}
|
|
9426
|
+
if (!isUsed) {
|
|
9427
|
+
const lines = content.split(`
|
|
9428
|
+
`);
|
|
9429
|
+
for (const line of lines) {
|
|
9430
|
+
if (isCommentOrString(line))
|
|
9431
|
+
continue;
|
|
9432
|
+
const cleanLine = stripStringsAndComments(line);
|
|
9433
|
+
const codeUsagePattern = new RegExp(`\\b${exp.name}\\s*[({]|\\b${exp.name}\\s*\\.|\\b${exp.name}\\s*,|\\b${exp.name}\\s*;|\\b${exp.name}\\s*\\)`);
|
|
9434
|
+
if (codeUsagePattern.test(cleanLine)) {
|
|
9435
|
+
isUsed = true;
|
|
9436
|
+
break;
|
|
9437
|
+
}
|
|
9438
|
+
}
|
|
9439
|
+
}
|
|
9440
|
+
if (isUsed)
|
|
9441
|
+
break;
|
|
9405
9442
|
}
|
|
9406
9443
|
if (!isUsed) {
|
|
9407
|
-
unusedExports.push(exp);
|
|
9444
|
+
unusedExports.push({ ...exp, usedInternally });
|
|
9408
9445
|
}
|
|
9409
9446
|
}
|
|
9410
9447
|
}
|
|
@@ -9415,6 +9452,24 @@ async function scanUnusedExports(config) {
|
|
|
9415
9452
|
exports: unusedExports
|
|
9416
9453
|
};
|
|
9417
9454
|
}
|
|
9455
|
+
function isCommentOrString(line) {
|
|
9456
|
+
const trimmed = line.trim();
|
|
9457
|
+
if (trimmed.startsWith("//"))
|
|
9458
|
+
return true;
|
|
9459
|
+
if (trimmed.startsWith("/*") || trimmed.startsWith("*"))
|
|
9460
|
+
return true;
|
|
9461
|
+
if (trimmed.includes("{/*") || trimmed.includes("*/}"))
|
|
9462
|
+
return true;
|
|
9463
|
+
return false;
|
|
9464
|
+
}
|
|
9465
|
+
function stripStringsAndComments(line) {
|
|
9466
|
+
let result = line;
|
|
9467
|
+
result = result.replace(/\/\/.*$/g, "");
|
|
9468
|
+
result = result.replace(/'([^'\\]|\\.)*'/g, "''");
|
|
9469
|
+
result = result.replace(/"([^"\\]|\\.)*"/g, '""');
|
|
9470
|
+
result = result.replace(/`([^`\\]|\\.)*`/g, "``");
|
|
9471
|
+
return result;
|
|
9472
|
+
}
|
|
9418
9473
|
|
|
9419
9474
|
// src/scanner.ts
|
|
9420
9475
|
function extractRoutePath(filePath) {
|
|
@@ -9742,7 +9797,7 @@ function findConfigFile(dir) {
|
|
|
9742
9797
|
}
|
|
9743
9798
|
|
|
9744
9799
|
// src/fixer.ts
|
|
9745
|
-
import { readFileSync as readFileSync6, writeFileSync } from "node:fs";
|
|
9800
|
+
import { readFileSync as readFileSync6, writeFileSync, unlinkSync } from "node:fs";
|
|
9746
9801
|
import { join as join6 } from "node:path";
|
|
9747
9802
|
function removeExportFromLine(rootDir, exp) {
|
|
9748
9803
|
const fullPath = join6(rootDir, exp.file);
|
|
@@ -9751,6 +9806,20 @@ function removeExportFromLine(rootDir, exp) {
|
|
|
9751
9806
|
const lines = content.split(`
|
|
9752
9807
|
`);
|
|
9753
9808
|
const lineIndex = exp.line - 1;
|
|
9809
|
+
if (!exp.usedInternally) {
|
|
9810
|
+
const deletedLines = deleteDeclaration(lines, lineIndex);
|
|
9811
|
+
if (deletedLines > 0) {
|
|
9812
|
+
const newContent = lines.join(`
|
|
9813
|
+
`);
|
|
9814
|
+
if (isFileEmpty(newContent)) {
|
|
9815
|
+
unlinkSync(fullPath);
|
|
9816
|
+
return true;
|
|
9817
|
+
}
|
|
9818
|
+
writeFileSync(fullPath, newContent, "utf-8");
|
|
9819
|
+
return true;
|
|
9820
|
+
}
|
|
9821
|
+
return false;
|
|
9822
|
+
}
|
|
9754
9823
|
const originalLine = lines[lineIndex];
|
|
9755
9824
|
const exportPrefixRegex = /^(export\s+(?:async\s+)?)/;
|
|
9756
9825
|
if (exportPrefixRegex.test(originalLine.trim())) {
|
|
@@ -9766,13 +9835,61 @@ function removeExportFromLine(rootDir, exp) {
|
|
|
9766
9835
|
return false;
|
|
9767
9836
|
}
|
|
9768
9837
|
}
|
|
9838
|
+
function deleteDeclaration(lines, startLine) {
|
|
9839
|
+
if (startLine >= lines.length)
|
|
9840
|
+
return 0;
|
|
9841
|
+
let endLine = startLine;
|
|
9842
|
+
let braceCount = 0;
|
|
9843
|
+
let foundClosing = false;
|
|
9844
|
+
for (let i = startLine;i < lines.length; i++) {
|
|
9845
|
+
const line = lines[i];
|
|
9846
|
+
const openBraces = (line.match(/{/g) || []).length;
|
|
9847
|
+
const closeBraces = (line.match(/}/g) || []).length;
|
|
9848
|
+
braceCount += openBraces - closeBraces;
|
|
9849
|
+
if (braceCount === 0) {
|
|
9850
|
+
if (line.includes(";") || line.includes("};")) {
|
|
9851
|
+
endLine = i;
|
|
9852
|
+
foundClosing = true;
|
|
9853
|
+
break;
|
|
9854
|
+
}
|
|
9855
|
+
if (i > startLine && line.trim() === "}") {
|
|
9856
|
+
endLine = i;
|
|
9857
|
+
foundClosing = true;
|
|
9858
|
+
break;
|
|
9859
|
+
}
|
|
9860
|
+
}
|
|
9861
|
+
}
|
|
9862
|
+
if (!foundClosing && braceCount === 0) {
|
|
9863
|
+
endLine = startLine;
|
|
9864
|
+
}
|
|
9865
|
+
const linesToDelete = endLine - startLine + 1;
|
|
9866
|
+
lines.splice(startLine, linesToDelete);
|
|
9867
|
+
return linesToDelete;
|
|
9868
|
+
}
|
|
9869
|
+
function isFileEmpty(content) {
|
|
9870
|
+
const lines = content.split(`
|
|
9871
|
+
`);
|
|
9872
|
+
for (const line of lines) {
|
|
9873
|
+
const trimmed = line.trim();
|
|
9874
|
+
if (!trimmed)
|
|
9875
|
+
continue;
|
|
9876
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*"))
|
|
9877
|
+
continue;
|
|
9878
|
+
if (trimmed.startsWith("import ") || trimmed.startsWith("export "))
|
|
9879
|
+
continue;
|
|
9880
|
+
if (trimmed === '"use client";' || trimmed === '"use server";' || trimmed === "'use client';" || trimmed === "'use server';")
|
|
9881
|
+
continue;
|
|
9882
|
+
return false;
|
|
9883
|
+
}
|
|
9884
|
+
return true;
|
|
9885
|
+
}
|
|
9769
9886
|
|
|
9770
9887
|
// src/init.ts
|
|
9771
|
-
import { writeFileSync as writeFileSync2, existsSync as
|
|
9888
|
+
import { writeFileSync as writeFileSync2, existsSync as existsSync6 } from "node:fs";
|
|
9772
9889
|
import { join as join7 } from "node:path";
|
|
9773
9890
|
function init(cwd = process.cwd()) {
|
|
9774
9891
|
const configPath = join7(cwd, "pruny.config.json");
|
|
9775
|
-
if (
|
|
9892
|
+
if (existsSync6(configPath)) {
|
|
9776
9893
|
console.log(source_default.yellow("⚠️ pruny.config.json already exists. Skipping."));
|
|
9777
9894
|
return;
|
|
9778
9895
|
}
|
|
@@ -10005,11 +10122,21 @@ program2.action(async (options) => {
|
|
|
10005
10122
|
if (result.unusedExports && result.unusedExports.exports.length > 0) {
|
|
10006
10123
|
console.log(source_default.yellow.bold(`\uD83D\uDD27 Fixing unused exports (removing "export" keyword)...
|
|
10007
10124
|
`));
|
|
10008
|
-
|
|
10125
|
+
const exportsByFile = new Map;
|
|
10009
10126
|
for (const exp of result.unusedExports.exports) {
|
|
10010
|
-
if (
|
|
10011
|
-
|
|
10012
|
-
|
|
10127
|
+
if (!exportsByFile.has(exp.file)) {
|
|
10128
|
+
exportsByFile.set(exp.file, []);
|
|
10129
|
+
}
|
|
10130
|
+
exportsByFile.get(exp.file).push(exp);
|
|
10131
|
+
}
|
|
10132
|
+
let fixedCount = 0;
|
|
10133
|
+
for (const [file, exports] of exportsByFile.entries()) {
|
|
10134
|
+
const sortedExports = exports.sort((a, b) => b.line - a.line);
|
|
10135
|
+
for (const exp of sortedExports) {
|
|
10136
|
+
if (removeExportFromLine(config.dir, exp)) {
|
|
10137
|
+
console.log(source_default.green(` Fixed: ${exp.name} in ${exp.file}`));
|
|
10138
|
+
fixedCount++;
|
|
10139
|
+
}
|
|
10013
10140
|
}
|
|
10014
10141
|
}
|
|
10015
10142
|
if (fixedCount > 0) {
|