cleanwind 0.3.0 → 0.3.2
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 +255 -30
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
+
import path4 from "path";
|
|
4
5
|
import { Command } from "commander";
|
|
5
6
|
|
|
6
7
|
// ../core/dist/index.js
|
|
@@ -522,6 +523,7 @@ var displayClasses = /* @__PURE__ */ new Set([
|
|
|
522
523
|
"hidden"
|
|
523
524
|
]);
|
|
524
525
|
var positionClasses = /* @__PURE__ */ new Set(["static", "fixed", "absolute", "relative", "sticky"]);
|
|
526
|
+
var borderStyleClasses = /* @__PURE__ */ new Set(["solid", "dashed", "dotted", "double", "hidden", "none"]);
|
|
525
527
|
var variantOrder = [
|
|
526
528
|
"sm",
|
|
527
529
|
"md",
|
|
@@ -614,8 +616,9 @@ function conflictKey(className) {
|
|
|
614
616
|
if (utility.startsWith("rounded")) {
|
|
615
617
|
return `${variantPrefix}${roundedConflictKey(utility)}`;
|
|
616
618
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
+
const border = borderConflictKey(utility);
|
|
620
|
+
if (border) {
|
|
621
|
+
return `${variantPrefix}${border}`;
|
|
619
622
|
}
|
|
620
623
|
if (/^flex-(row|row-reverse|col|col-reverse)$/u.test(utility)) {
|
|
621
624
|
return `${variantPrefix}flex-direction`;
|
|
@@ -702,8 +705,26 @@ function roundedConflictKey(utility) {
|
|
|
702
705
|
return match?.groups?.side ? `border-radius-${match.groups.side}` : "border-radius";
|
|
703
706
|
}
|
|
704
707
|
function borderConflictKey(utility) {
|
|
705
|
-
|
|
706
|
-
|
|
708
|
+
if (utility === "border-collapse" || utility === "border-separate") {
|
|
709
|
+
return "border-collapse";
|
|
710
|
+
}
|
|
711
|
+
const match = /^border(?:-(?<side>[trblxy]))?(?:-(?<value>.+))?$/u.exec(utility);
|
|
712
|
+
if (!match?.groups) {
|
|
713
|
+
return void 0;
|
|
714
|
+
}
|
|
715
|
+
const side = match.groups.side;
|
|
716
|
+
const value = match.groups.value;
|
|
717
|
+
const sideSuffix = side ? `-${side}` : "";
|
|
718
|
+
if (!value || isBorderWidthValue(value)) {
|
|
719
|
+
return `border-width${sideSuffix}`;
|
|
720
|
+
}
|
|
721
|
+
if (!side && borderStyleClasses.has(value)) {
|
|
722
|
+
return "border-style";
|
|
723
|
+
}
|
|
724
|
+
return `border-color${sideSuffix}`;
|
|
725
|
+
}
|
|
726
|
+
function isBorderWidthValue(value) {
|
|
727
|
+
return /^(0|2|4|8|\d+|\[.+\])$/u.test(value);
|
|
707
728
|
}
|
|
708
729
|
function rankUtility(utility) {
|
|
709
730
|
if (utility.startsWith("container")) return 0;
|
|
@@ -1069,30 +1090,47 @@ function hasParseIssue(issues) {
|
|
|
1069
1090
|
}
|
|
1070
1091
|
|
|
1071
1092
|
// src/index.ts
|
|
1093
|
+
var defaultListLimit = 12;
|
|
1094
|
+
var tree = {
|
|
1095
|
+
branch: "\u251C\u2500",
|
|
1096
|
+
last: "\u2514\u2500",
|
|
1097
|
+
pipe: "\u2502",
|
|
1098
|
+
tee: "\u2502 \u251C",
|
|
1099
|
+
elbow: "\u2502 \u2514",
|
|
1100
|
+
spacer: "\u2502 ",
|
|
1101
|
+
step: "*",
|
|
1102
|
+
primary: "\u25C6",
|
|
1103
|
+
secondary: "\u25CF"
|
|
1104
|
+
};
|
|
1072
1105
|
var program = new Command();
|
|
1073
|
-
program.name("cleanwind").description("Clean imports and Tailwind CSS class names.").version("0.3.
|
|
1106
|
+
program.name("cleanwind").description("Clean imports and Tailwind CSS class names.").version("0.3.2");
|
|
1074
1107
|
program.command("check").description("Check files without writing changes.").option("--cwd <path>", "Working directory").option("--config <path>", "Path to cleanwind config").option("--write", "Write fixes while running check").option("--check", "Force check mode", true).option("--verbose", "Print detailed diagnostics").option("--format", "Format output with Prettier after cleanup").action(async (options) => {
|
|
1075
1108
|
const runOptions = toRunOptions(options);
|
|
1076
1109
|
if (options.write) {
|
|
1077
|
-
const result2 = await
|
|
1078
|
-
|
|
1110
|
+
const result2 = await runWithProgress(
|
|
1111
|
+
"Fixing project",
|
|
1112
|
+
() => fix({ ...runOptions, write: true })
|
|
1113
|
+
);
|
|
1114
|
+
printFixResult(result2, options.verbose ?? false, runOptions.cwd ?? process.cwd());
|
|
1079
1115
|
process.exitCode = result2.conflicts.length > 0 ? 1 : 0;
|
|
1080
1116
|
return;
|
|
1081
1117
|
}
|
|
1082
|
-
const result = await check(runOptions);
|
|
1083
|
-
printCheckResult(result, options.verbose ?? false);
|
|
1118
|
+
const result = await runWithProgress("Checking project", () => check(runOptions));
|
|
1119
|
+
printCheckResult(result, options.verbose ?? false, runOptions.cwd ?? process.cwd());
|
|
1084
1120
|
process.exitCode = result.ok ? 0 : 1;
|
|
1085
1121
|
});
|
|
1086
1122
|
program.command("fix").description("Fix files in place.").option("--cwd <path>", "Working directory").option("--config <path>", "Path to cleanwind config").option("--write", "Write fixes", true).option("--check", "Preview fixes without writing").option("--verbose", "Print detailed diagnostics").option("--staged", "Only fix staged files").option("--format", "Format output with Prettier after cleanup").action(async (options) => {
|
|
1087
|
-
const result = await
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1123
|
+
const result = await runWithProgress(
|
|
1124
|
+
options.check ? "Previewing cleanup" : "Fixing project",
|
|
1125
|
+
() => fix({
|
|
1126
|
+
...toRunOptions(options),
|
|
1127
|
+
write: options.check ? false : options.write ?? true,
|
|
1128
|
+
staged: options.staged ?? false
|
|
1129
|
+
})
|
|
1130
|
+
);
|
|
1131
|
+
printFixResult(result, options.verbose ?? false, options.cwd ?? process.cwd());
|
|
1093
1132
|
process.exitCode = result.conflicts.length > 0 ? 1 : 0;
|
|
1094
1133
|
});
|
|
1095
|
-
await program.parseAsync();
|
|
1096
1134
|
function toRunOptions(options) {
|
|
1097
1135
|
const runOptions = {};
|
|
1098
1136
|
if (options.cwd !== void 0) runOptions.cwd = options.cwd;
|
|
@@ -1104,31 +1142,218 @@ function toRunOptions(options) {
|
|
|
1104
1142
|
if (options.format !== void 0) runOptions.format = options.format;
|
|
1105
1143
|
return runOptions;
|
|
1106
1144
|
}
|
|
1107
|
-
function
|
|
1145
|
+
async function runWithProgress(label, task) {
|
|
1146
|
+
if (!shouldRenderProgress()) {
|
|
1147
|
+
return task();
|
|
1148
|
+
}
|
|
1149
|
+
let progress = 0;
|
|
1150
|
+
const timer = setInterval(() => {
|
|
1151
|
+
progress = nextProgress(progress);
|
|
1152
|
+
renderProgress(label, progress);
|
|
1153
|
+
}, 90);
|
|
1154
|
+
renderProgress(label, progress);
|
|
1155
|
+
try {
|
|
1156
|
+
const result = await task();
|
|
1157
|
+
progress = 100;
|
|
1158
|
+
renderProgress(label, progress);
|
|
1159
|
+
await wait(120);
|
|
1160
|
+
return result;
|
|
1161
|
+
} finally {
|
|
1162
|
+
clearInterval(timer);
|
|
1163
|
+
clearProgress();
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
function nextProgress(progress) {
|
|
1167
|
+
if (progress < 55) {
|
|
1168
|
+
return progress + 7;
|
|
1169
|
+
}
|
|
1170
|
+
if (progress < 85) {
|
|
1171
|
+
return progress + 3;
|
|
1172
|
+
}
|
|
1173
|
+
return Math.min(progress + 1, 95);
|
|
1174
|
+
}
|
|
1175
|
+
function renderProgress(label, progress) {
|
|
1176
|
+
const width = 24;
|
|
1177
|
+
const filled = Math.round(progress / 100 * width);
|
|
1178
|
+
const bar = `${"\u2588".repeat(filled)}${"\u2591".repeat(width - filled)}`;
|
|
1179
|
+
process.stdout.write(
|
|
1180
|
+
`\r${color.cyan("cleanwind")} ${label} ${color.green(bar)} ${String(progress).padStart(3, " ")}%`
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
function clearProgress() {
|
|
1184
|
+
process.stdout.write("\r\x1B[2K");
|
|
1185
|
+
}
|
|
1186
|
+
function shouldRenderProgress() {
|
|
1187
|
+
return process.stdout.isTTY === true && process.env.CI === void 0;
|
|
1188
|
+
}
|
|
1189
|
+
function wait(milliseconds) {
|
|
1190
|
+
return new Promise((resolve) => {
|
|
1191
|
+
setTimeout(resolve, milliseconds);
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
function printCheckResult(result, verbose, cwd) {
|
|
1108
1195
|
if (result.ok) {
|
|
1109
|
-
|
|
1196
|
+
printHeader("check");
|
|
1197
|
+
printStep("Loaded config", "done");
|
|
1198
|
+
printStep("Checked project", "done");
|
|
1199
|
+
printStep("Built report", "done");
|
|
1200
|
+
printSpacer();
|
|
1201
|
+
console.log(`${color.green(tree.primary)} ${color.bold("All files are clean")}`);
|
|
1110
1202
|
return;
|
|
1111
1203
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1204
|
+
printHeader("check");
|
|
1205
|
+
printStep("Loaded config", "done");
|
|
1206
|
+
printStep("Checked project", "done");
|
|
1207
|
+
printStep("Built report", "done");
|
|
1208
|
+
printSpacer();
|
|
1209
|
+
printMetrics(
|
|
1210
|
+
result.changedFiles.length,
|
|
1211
|
+
result.changedFiles.length === 1 ? "file needs cleanup" : "files need cleanup",
|
|
1212
|
+
result.conflicts.length
|
|
1213
|
+
);
|
|
1214
|
+
printFileList("Files", result.changedFiles, cwd, verbose);
|
|
1215
|
+
printDiagnostics(result, verbose, cwd);
|
|
1216
|
+
printNextStep(result);
|
|
1217
|
+
}
|
|
1218
|
+
function printFixResult(result, verbose, cwd) {
|
|
1116
1219
|
const written = result.writtenFiles.length;
|
|
1117
1220
|
const changed = result.changedFiles.length;
|
|
1118
1221
|
const mode = written > 0 ? "fixed" : "would change";
|
|
1119
|
-
|
|
1120
|
-
|
|
1222
|
+
printHeader("fix");
|
|
1223
|
+
printStep("Loaded config", "done");
|
|
1224
|
+
printStep(written > 0 ? "Applied cleanup" : "Previewed cleanup", "done");
|
|
1225
|
+
printStep("Built report", "done");
|
|
1226
|
+
printSpacer();
|
|
1227
|
+
printMetrics(
|
|
1228
|
+
written > 0 ? written : changed,
|
|
1229
|
+
mode === "fixed" ? written === 1 ? "file fixed" : "files fixed" : changed === 1 ? "file would change" : "files would change",
|
|
1230
|
+
result.conflicts.length
|
|
1231
|
+
);
|
|
1232
|
+
printFileList(written > 0 ? "Written" : "Would change", written > 0 ? result.writtenFiles : result.changedFiles, cwd, verbose);
|
|
1233
|
+
printDiagnostics(result, verbose, cwd);
|
|
1234
|
+
printNextStep(result);
|
|
1235
|
+
}
|
|
1236
|
+
function printHeader(command) {
|
|
1237
|
+
console.log(`${color.cyan("cleanwind")} ${color.bold(command)}`);
|
|
1238
|
+
console.log(tree.pipe);
|
|
1239
|
+
}
|
|
1240
|
+
function printStep(label, status) {
|
|
1241
|
+
console.log(`${tree.branch} ${color.green(tree.step)} ${label} - ${color.bold(status)}`);
|
|
1242
|
+
}
|
|
1243
|
+
function printSpacer() {
|
|
1244
|
+
console.log(tree.pipe);
|
|
1245
|
+
}
|
|
1246
|
+
function printMetrics(fileCount, fileLabel, conflictCount) {
|
|
1247
|
+
console.log(`${color.green(tree.primary)} ${color.bold(`${fileCount} ${fileLabel}`)}`);
|
|
1248
|
+
if (conflictCount > 0) {
|
|
1249
|
+
console.log(
|
|
1250
|
+
`${color.yellow(tree.secondary)} ${color.bold(`${conflictCount} conflict${conflictCount === 1 ? "" : "s"}`)} need review`
|
|
1251
|
+
);
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
console.log(`${color.green(tree.secondary)} ${color.bold("0 conflicts")} found`);
|
|
1255
|
+
}
|
|
1256
|
+
function printFileList(title, files, cwd, verbose) {
|
|
1257
|
+
if (files.length === 0) {
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
const shown = verbose ? files : files.slice(0, defaultListLimit);
|
|
1261
|
+
printSpacer();
|
|
1262
|
+
console.log(`${tree.branch} ${color.bold(title)}`);
|
|
1263
|
+
for (const [index, file] of shown.entries()) {
|
|
1264
|
+
const prefix = index === shown.length - 1 && (verbose || files.length === shown.length) ? tree.elbow : tree.tee;
|
|
1265
|
+
console.log(`${prefix} ${formatPath(file, cwd)}`);
|
|
1266
|
+
}
|
|
1267
|
+
if (!verbose && files.length > shown.length) {
|
|
1268
|
+
console.log(
|
|
1269
|
+
`${tree.elbow} ${color.dim(`${files.length - shown.length} more. Run with --verbose to show all.`)}`
|
|
1270
|
+
);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
function printDiagnostics(result, verbose, cwd) {
|
|
1274
|
+
printConflicts(result, verbose, cwd);
|
|
1275
|
+
printIssues(result, verbose, cwd);
|
|
1121
1276
|
}
|
|
1122
|
-
function
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1277
|
+
function printConflicts(result, verbose, cwd) {
|
|
1278
|
+
if (result.conflicts.length === 0) {
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
const conflicts = verbose ? result.conflicts : result.conflicts.slice(0, defaultListLimit);
|
|
1282
|
+
const groups = groupConflictsByFile(conflicts, cwd);
|
|
1283
|
+
printSpacer();
|
|
1284
|
+
console.log(`${tree.branch} ${color.bold("Conflicts")}`);
|
|
1285
|
+
for (const [groupIndex, group] of groups.entries()) {
|
|
1286
|
+
const isLastGroup = groupIndex === groups.length - 1 && (verbose || result.conflicts.length === conflicts.length);
|
|
1287
|
+
console.log(`${isLastGroup ? tree.elbow : tree.tee} ${color.cyan(group.file)}`);
|
|
1288
|
+
for (const [conflictIndex, conflict] of group.conflicts.entries()) {
|
|
1289
|
+
const isLastConflict = conflictIndex === group.conflicts.length - 1;
|
|
1290
|
+
console.log(
|
|
1291
|
+
`${tree.spacer}${isLastConflict ? "\u2514" : "\u251C"} L${conflict.line} ${conflict.conflictingClasses.join(" + ")}`
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
if (!verbose && result.conflicts.length > conflicts.length) {
|
|
1296
|
+
console.log(
|
|
1297
|
+
`${tree.elbow} ${color.dim(`${result.conflicts.length - conflicts.length} more conflict(s). Run with --verbose to show all.`)}`
|
|
1126
1298
|
);
|
|
1127
1299
|
}
|
|
1300
|
+
}
|
|
1301
|
+
function printIssues(result, verbose, cwd) {
|
|
1128
1302
|
if (!verbose) {
|
|
1129
1303
|
return;
|
|
1130
1304
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1305
|
+
const issues = result.issues.filter((issue) => issue.kind !== "conflict");
|
|
1306
|
+
if (issues.length === 0) {
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
printSpacer();
|
|
1310
|
+
console.log(`${tree.branch} ${color.bold("Diagnostics")}`);
|
|
1311
|
+
for (const [index, issue] of issues.entries()) {
|
|
1312
|
+
const prefix = index === issues.length - 1 ? tree.elbow : tree.tee;
|
|
1313
|
+
console.log(
|
|
1314
|
+
`${prefix} ${color.cyan(formatPath(issue.file, cwd))}:${issue.line} ${color.dim(`[${issue.kind}]`)} ${issue.message}`
|
|
1315
|
+
);
|
|
1133
1316
|
}
|
|
1134
1317
|
}
|
|
1318
|
+
function printNextStep(result) {
|
|
1319
|
+
if (result.conflicts.length > 0) {
|
|
1320
|
+
printSpacer();
|
|
1321
|
+
console.log(`${tree.last} ${color.bold("Next:")} Resolve conflicts, then run ${color.cyan("npx cleanwind fix --format")}`);
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
if (result.changedFiles.length > 0) {
|
|
1325
|
+
printSpacer();
|
|
1326
|
+
console.log(`${tree.last} ${color.bold("Next:")} Apply cleanup with ${color.cyan("npx cleanwind fix --format")}`);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
function formatPath(file, cwd) {
|
|
1330
|
+
const relative = path4.relative(path4.resolve(cwd), file);
|
|
1331
|
+
return (relative && !relative.startsWith("..") ? relative : file).replaceAll("\\", "/");
|
|
1332
|
+
}
|
|
1333
|
+
function groupConflictsByFile(conflicts, cwd) {
|
|
1334
|
+
const groups = [];
|
|
1335
|
+
for (const conflict of conflicts) {
|
|
1336
|
+
const file = formatPath(conflict.file, cwd);
|
|
1337
|
+
const current = groups.at(-1);
|
|
1338
|
+
if (current?.file === file) {
|
|
1339
|
+
current.conflicts.push(conflict);
|
|
1340
|
+
} else {
|
|
1341
|
+
groups.push({ file, conflicts: [conflict] });
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
return groups;
|
|
1345
|
+
}
|
|
1346
|
+
function supportsColor() {
|
|
1347
|
+
return process.env.NO_COLOR === void 0 && process.stdout.isTTY === true;
|
|
1348
|
+
}
|
|
1349
|
+
function paint(code, value) {
|
|
1350
|
+
return supportsColor() ? `\x1B[${code}m${value}\x1B[0m` : value;
|
|
1351
|
+
}
|
|
1352
|
+
var color = {
|
|
1353
|
+
bold: (value) => paint("1", value),
|
|
1354
|
+
cyan: (value) => paint("36", value),
|
|
1355
|
+
dim: (value) => paint("2", value),
|
|
1356
|
+
green: (value) => paint("32", value),
|
|
1357
|
+
yellow: (value) => paint("33", value)
|
|
1358
|
+
};
|
|
1359
|
+
await program.parseAsync();
|