cleanwind 0.3.1 → 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 +162 -52
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1091,26 +1091,43 @@ function hasParseIssue(issues) {
|
|
|
1091
1091
|
|
|
1092
1092
|
// src/index.ts
|
|
1093
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
|
+
};
|
|
1094
1105
|
var program = new Command();
|
|
1095
|
-
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");
|
|
1096
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) => {
|
|
1097
1108
|
const runOptions = toRunOptions(options);
|
|
1098
1109
|
if (options.write) {
|
|
1099
|
-
const result2 = await
|
|
1110
|
+
const result2 = await runWithProgress(
|
|
1111
|
+
"Fixing project",
|
|
1112
|
+
() => fix({ ...runOptions, write: true })
|
|
1113
|
+
);
|
|
1100
1114
|
printFixResult(result2, options.verbose ?? false, runOptions.cwd ?? process.cwd());
|
|
1101
1115
|
process.exitCode = result2.conflicts.length > 0 ? 1 : 0;
|
|
1102
1116
|
return;
|
|
1103
1117
|
}
|
|
1104
|
-
const result = await check(runOptions);
|
|
1118
|
+
const result = await runWithProgress("Checking project", () => check(runOptions));
|
|
1105
1119
|
printCheckResult(result, options.verbose ?? false, runOptions.cwd ?? process.cwd());
|
|
1106
1120
|
process.exitCode = result.ok ? 0 : 1;
|
|
1107
1121
|
});
|
|
1108
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) => {
|
|
1109
|
-
const result = await
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
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
|
+
);
|
|
1114
1131
|
printFixResult(result, options.verbose ?? false, options.cwd ?? process.cwd());
|
|
1115
1132
|
process.exitCode = result.conflicts.length > 0 ? 1 : 0;
|
|
1116
1133
|
});
|
|
@@ -1125,18 +1142,74 @@ function toRunOptions(options) {
|
|
|
1125
1142
|
if (options.format !== void 0) runOptions.format = options.format;
|
|
1126
1143
|
return runOptions;
|
|
1127
1144
|
}
|
|
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
|
+
}
|
|
1128
1194
|
function printCheckResult(result, verbose, cwd) {
|
|
1129
1195
|
if (result.ok) {
|
|
1130
|
-
|
|
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")}`);
|
|
1131
1202
|
return;
|
|
1132
1203
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
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
|
|
1140
1213
|
);
|
|
1141
1214
|
printFileList("Files", result.changedFiles, cwd, verbose);
|
|
1142
1215
|
printDiagnostics(result, verbose, cwd);
|
|
@@ -1146,30 +1219,55 @@ function printFixResult(result, verbose, cwd) {
|
|
|
1146
1219
|
const written = result.writtenFiles.length;
|
|
1147
1220
|
const changed = result.changedFiles.length;
|
|
1148
1221
|
const mode = written > 0 ? "fixed" : "would change";
|
|
1149
|
-
|
|
1150
|
-
|
|
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
|
+
);
|
|
1151
1232
|
printFileList(written > 0 ? "Written" : "Would change", written > 0 ? result.writtenFiles : result.changedFiles, cwd, verbose);
|
|
1152
1233
|
printDiagnostics(result, verbose, cwd);
|
|
1153
1234
|
printNextStep(result);
|
|
1154
1235
|
}
|
|
1155
|
-
function
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
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`);
|
|
1160
1255
|
}
|
|
1161
1256
|
function printFileList(title, files, cwd, verbose) {
|
|
1162
1257
|
if (files.length === 0) {
|
|
1163
1258
|
return;
|
|
1164
1259
|
}
|
|
1165
1260
|
const shown = verbose ? files : files.slice(0, defaultListLimit);
|
|
1166
|
-
|
|
1167
|
-
console.log(color.bold(title));
|
|
1168
|
-
for (const file of shown) {
|
|
1169
|
-
|
|
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)}`);
|
|
1170
1266
|
}
|
|
1171
1267
|
if (!verbose && files.length > shown.length) {
|
|
1172
|
-
console.log(
|
|
1268
|
+
console.log(
|
|
1269
|
+
`${tree.elbow} ${color.dim(`${files.length - shown.length} more. Run with --verbose to show all.`)}`
|
|
1270
|
+
);
|
|
1173
1271
|
}
|
|
1174
1272
|
}
|
|
1175
1273
|
function printDiagnostics(result, verbose, cwd) {
|
|
@@ -1181,24 +1279,22 @@ function printConflicts(result, verbose, cwd) {
|
|
|
1181
1279
|
return;
|
|
1182
1280
|
}
|
|
1183
1281
|
const conflicts = verbose ? result.conflicts : result.conflicts.slice(0, defaultListLimit);
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
console.
|
|
1187
|
-
for (const
|
|
1188
|
-
const
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
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
|
+
);
|
|
1192
1293
|
}
|
|
1193
|
-
console.error(
|
|
1194
|
-
` ${color.dim(String(conflict.line).padStart(4, " "))} ${conflict.conflictingClasses.join(" + ")}`
|
|
1195
|
-
);
|
|
1196
1294
|
}
|
|
1197
1295
|
if (!verbose && result.conflicts.length > conflicts.length) {
|
|
1198
|
-
console.
|
|
1199
|
-
color.dim(
|
|
1200
|
-
` ... ${result.conflicts.length - conflicts.length} more conflict(s). Run with --verbose to show all.`
|
|
1201
|
-
)
|
|
1296
|
+
console.log(
|
|
1297
|
+
`${tree.elbow} ${color.dim(`${result.conflicts.length - conflicts.length} more conflict(s). Run with --verbose to show all.`)}`
|
|
1202
1298
|
);
|
|
1203
1299
|
}
|
|
1204
1300
|
}
|
|
@@ -1210,29 +1306,43 @@ function printIssues(result, verbose, cwd) {
|
|
|
1210
1306
|
if (issues.length === 0) {
|
|
1211
1307
|
return;
|
|
1212
1308
|
}
|
|
1213
|
-
|
|
1214
|
-
console.
|
|
1215
|
-
for (const issue of issues) {
|
|
1216
|
-
|
|
1217
|
-
|
|
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}`
|
|
1218
1315
|
);
|
|
1219
1316
|
}
|
|
1220
1317
|
}
|
|
1221
1318
|
function printNextStep(result) {
|
|
1222
1319
|
if (result.conflicts.length > 0) {
|
|
1223
|
-
|
|
1224
|
-
console.log(color.
|
|
1320
|
+
printSpacer();
|
|
1321
|
+
console.log(`${tree.last} ${color.bold("Next:")} Resolve conflicts, then run ${color.cyan("npx cleanwind fix --format")}`);
|
|
1225
1322
|
return;
|
|
1226
1323
|
}
|
|
1227
1324
|
if (result.changedFiles.length > 0) {
|
|
1228
|
-
|
|
1229
|
-
console.log(color.
|
|
1325
|
+
printSpacer();
|
|
1326
|
+
console.log(`${tree.last} ${color.bold("Next:")} Apply cleanup with ${color.cyan("npx cleanwind fix --format")}`);
|
|
1230
1327
|
}
|
|
1231
1328
|
}
|
|
1232
1329
|
function formatPath(file, cwd) {
|
|
1233
1330
|
const relative = path4.relative(path4.resolve(cwd), file);
|
|
1234
1331
|
return (relative && !relative.startsWith("..") ? relative : file).replaceAll("\\", "/");
|
|
1235
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
|
+
}
|
|
1236
1346
|
function supportsColor() {
|
|
1237
1347
|
return process.env.NO_COLOR === void 0 && process.stdout.isTTY === true;
|
|
1238
1348
|
}
|