sweeply 0.1.0 → 0.2.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/cli.js +166 -21
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.tsx
|
|
4
4
|
import { render } from "ink";
|
|
5
|
-
import
|
|
5
|
+
import os4 from "os";
|
|
6
6
|
|
|
7
7
|
// src/ui/App.tsx
|
|
8
8
|
import { useEffect, useMemo, useState } from "react";
|
|
9
9
|
import { Box as Box3, Text as Text3, useApp, useInput, useStdout } from "ink";
|
|
10
|
-
import
|
|
11
|
-
import
|
|
10
|
+
import path4 from "path";
|
|
11
|
+
import os3 from "os";
|
|
12
12
|
|
|
13
13
|
// src/core/scanner.ts
|
|
14
14
|
import fs from "fs/promises";
|
|
@@ -207,24 +207,141 @@ async function removeTargets(targets, onProgress) {
|
|
|
207
207
|
return { removed, failed };
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
// src/core/update.ts
|
|
211
|
+
import fs5 from "fs/promises";
|
|
212
|
+
import path3 from "path";
|
|
213
|
+
import os2 from "os";
|
|
214
|
+
import { fileURLToPath } from "url";
|
|
215
|
+
var PKG_NAME = "sweeply";
|
|
216
|
+
var REGISTRY = "https://registry.npmjs.org";
|
|
217
|
+
var CHECK_EVERY_MS = 24 * 60 * 60 * 1e3;
|
|
218
|
+
var FETCH_TIMEOUT_MS = 2e3;
|
|
219
|
+
async function readOwnVersion() {
|
|
220
|
+
let dir2 = path3.dirname(fileURLToPath(import.meta.url));
|
|
221
|
+
for (let i = 0; i < 5; i++) {
|
|
222
|
+
try {
|
|
223
|
+
const raw = await fs5.readFile(path3.join(dir2, "package.json"), "utf8");
|
|
224
|
+
const parsed = JSON.parse(raw);
|
|
225
|
+
if (parsed.name === PKG_NAME && parsed.version) return parsed.version;
|
|
226
|
+
} catch {
|
|
227
|
+
}
|
|
228
|
+
const parent = path3.dirname(dir2);
|
|
229
|
+
if (parent === dir2) break;
|
|
230
|
+
dir2 = parent;
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
function isNewer(latest, current) {
|
|
235
|
+
const nums = (v) => v.split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
236
|
+
const a = nums(latest);
|
|
237
|
+
const b = nums(current);
|
|
238
|
+
for (let i = 0; i < 3; i++) {
|
|
239
|
+
const x = a[i] ?? 0;
|
|
240
|
+
const y = b[i] ?? 0;
|
|
241
|
+
if (x !== y) return x > y;
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
function upgradeCommand() {
|
|
246
|
+
const self = process.argv[1] ?? fileURLToPath(import.meta.url);
|
|
247
|
+
if (self.includes("/Caskroom/") || self.includes("/Cellar/")) {
|
|
248
|
+
return `brew upgrade ${PKG_NAME}`;
|
|
249
|
+
}
|
|
250
|
+
if (self.includes("/_npx/")) return `npx ${PKG_NAME}@latest`;
|
|
251
|
+
if (self.includes("/.bun/")) return `bun add -g ${PKG_NAME}@latest`;
|
|
252
|
+
if (self.includes("/yarn/global/")) return `yarn global upgrade ${PKG_NAME}`;
|
|
253
|
+
return `npm i -g ${PKG_NAME}@latest`;
|
|
254
|
+
}
|
|
255
|
+
function cacheFile() {
|
|
256
|
+
const base = process.env.XDG_CACHE_HOME || path3.join(os2.homedir(), ".cache");
|
|
257
|
+
return path3.join(base, PKG_NAME, "update-check.json");
|
|
258
|
+
}
|
|
259
|
+
async function readCache() {
|
|
260
|
+
try {
|
|
261
|
+
const raw = await fs5.readFile(cacheFile(), "utf8");
|
|
262
|
+
const parsed = JSON.parse(raw);
|
|
263
|
+
if (typeof parsed.at === "number" && typeof parsed.latest === "string") {
|
|
264
|
+
return { at: parsed.at, latest: parsed.latest };
|
|
265
|
+
}
|
|
266
|
+
} catch {
|
|
267
|
+
}
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
async function writeCache(latest) {
|
|
271
|
+
const file = cacheFile();
|
|
272
|
+
await fs5.mkdir(path3.dirname(file), { recursive: true });
|
|
273
|
+
await fs5.writeFile(file, JSON.stringify({ at: Date.now(), latest }), "utf8");
|
|
274
|
+
}
|
|
275
|
+
async function fetchLatest() {
|
|
276
|
+
const res = await fetch(`${REGISTRY}/${PKG_NAME}/latest`, {
|
|
277
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
278
|
+
headers: { accept: "application/json" }
|
|
279
|
+
});
|
|
280
|
+
if (!res.ok) return null;
|
|
281
|
+
const body = await res.json();
|
|
282
|
+
return body.version ?? null;
|
|
283
|
+
}
|
|
284
|
+
async function checkForUpdate() {
|
|
285
|
+
try {
|
|
286
|
+
if (process.env.NO_UPDATE_NOTIFIER || process.env.CI) return null;
|
|
287
|
+
const current = await readOwnVersion();
|
|
288
|
+
if (!current) return null;
|
|
289
|
+
const cached = await readCache();
|
|
290
|
+
let latest = null;
|
|
291
|
+
if (cached && Date.now() - cached.at < CHECK_EVERY_MS) {
|
|
292
|
+
latest = cached.latest;
|
|
293
|
+
} else {
|
|
294
|
+
latest = await fetchLatest();
|
|
295
|
+
if (latest) void writeCache(latest).catch(() => {
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
if (!latest || !isNewer(latest, current)) return null;
|
|
299
|
+
return { current, latest, command: upgradeCommand() };
|
|
300
|
+
} catch {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
210
305
|
// src/ui/ProjectRow.tsx
|
|
211
306
|
import { Box, Text } from "ink";
|
|
212
307
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
213
308
|
var SEM_COLOR = { green: "green", yellow: "yellow", red: "red" };
|
|
214
|
-
|
|
309
|
+
var CURSOR_BG = "blackBright";
|
|
310
|
+
function ProjectRow({
|
|
311
|
+
row,
|
|
312
|
+
isCursor,
|
|
313
|
+
width
|
|
314
|
+
}) {
|
|
215
315
|
const targets = row.project.targets.map((t) => t.name).join(" + ");
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
316
|
+
const bg = isCursor ? CURSOR_BG : void 0;
|
|
317
|
+
const dim = isCursor ? "white" : "gray";
|
|
318
|
+
const marked = row.selected && !row.deleted;
|
|
319
|
+
const danger = isCursor ? "redBright" : "red";
|
|
320
|
+
const sem = row.git ? SEM_COLOR[row.git.semaphore] : dim;
|
|
321
|
+
const nameColor = row.deleted ? dim : marked ? danger : void 0;
|
|
322
|
+
const sizeColor = row.deleted ? dim : marked ? danger : "magenta";
|
|
323
|
+
const tailColor = marked ? danger : dim;
|
|
324
|
+
return /* @__PURE__ */ jsxs(Box, { width, backgroundColor: bg, overflow: "hidden", children: [
|
|
325
|
+
/* @__PURE__ */ jsx(Text, { color: marked ? danger : "cyan", backgroundColor: bg, bold: marked, children: isCursor ? "\u258C" : marked ? "\u2503" : " " }),
|
|
326
|
+
/* @__PURE__ */ jsx(Text, { backgroundColor: bg, color: row.deleted ? dim : marked ? danger : void 0, bold: marked, children: row.deleted ? " \u2714 " : marked ? " \u2717 " : " \u25A1 " }),
|
|
327
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(
|
|
328
|
+
Text,
|
|
329
|
+
{
|
|
330
|
+
backgroundColor: bg,
|
|
331
|
+
color: nameColor,
|
|
332
|
+
bold: isCursor || marked,
|
|
333
|
+
strikethrough: row.deleted,
|
|
334
|
+
wrap: "truncate",
|
|
335
|
+
children: row.project.name
|
|
336
|
+
}
|
|
337
|
+
) }),
|
|
338
|
+
/* @__PURE__ */ jsx(Box, { width: 9, justifyContent: "flex-end", marginRight: 2, children: /* @__PURE__ */ jsx(Text, { backgroundColor: bg, color: sizeColor, bold: marked, children: row.deleted ? "liberado" : formatSize(row.size) }) }),
|
|
339
|
+
/* @__PURE__ */ jsx(Box, { width: 16, children: /* @__PURE__ */ jsxs(Text, { backgroundColor: bg, color: sem, bold: marked, children: [
|
|
223
340
|
"\u25CF ",
|
|
224
341
|
row.git ? row.git.summary : "\u2026"
|
|
225
342
|
] }) }),
|
|
226
|
-
/* @__PURE__ */ jsx(Box, { width: 12, children: /* @__PURE__ */ jsx(Text, { color:
|
|
227
|
-
/* @__PURE__ */ jsx(Text, { color:
|
|
343
|
+
/* @__PURE__ */ jsx(Box, { width: 12, children: /* @__PURE__ */ jsx(Text, { backgroundColor: bg, color: tailColor, children: row.git?.lastActivity ?? "" }) }),
|
|
344
|
+
/* @__PURE__ */ jsx(Box, { flexGrow: 1, minWidth: 0, children: /* @__PURE__ */ jsx(Text, { backgroundColor: bg, color: tailColor, wrap: "truncate", children: targets }) })
|
|
228
345
|
] });
|
|
229
346
|
}
|
|
230
347
|
|
|
@@ -244,12 +361,12 @@ function Footer({
|
|
|
244
361
|
] }),
|
|
245
362
|
/* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
|
|
246
363
|
"\u2191\u2193 navegar \xB7 espacio seleccionar \xB7 a seleccionar \u{1F7E2} \xB7 enter borrar",
|
|
247
|
-
selectedCount > 0 && /* @__PURE__ */ jsxs2(Text2, { color: "
|
|
248
|
-
"
|
|
364
|
+
selectedCount > 0 && /* @__PURE__ */ jsxs2(Text2, { color: "red", bold: true, children: [
|
|
365
|
+
" ",
|
|
366
|
+
"\u2717 ",
|
|
249
367
|
selectedCount,
|
|
250
|
-
" = ",
|
|
251
|
-
formatSize(selectedSize)
|
|
252
|
-
")"
|
|
368
|
+
" marcados = ",
|
|
369
|
+
formatSize(selectedSize)
|
|
253
370
|
] }),
|
|
254
371
|
" \xB7 g detalle git \xB7 s orden",
|
|
255
372
|
sortBySize ? " (tama\xF1o)" : "",
|
|
@@ -271,6 +388,16 @@ function App({ rootDir, targets }) {
|
|
|
271
388
|
const [sortBySize, setSortBySize] = useState(false);
|
|
272
389
|
const [freedSize, setFreedSize] = useState(0);
|
|
273
390
|
const [deleteProgress, setDeleteProgress] = useState("");
|
|
391
|
+
const [update, setUpdate] = useState(null);
|
|
392
|
+
useEffect(() => {
|
|
393
|
+
let cancelled = false;
|
|
394
|
+
void checkForUpdate().then((info) => {
|
|
395
|
+
if (!cancelled && info) setUpdate(info);
|
|
396
|
+
});
|
|
397
|
+
return () => {
|
|
398
|
+
cancelled = true;
|
|
399
|
+
};
|
|
400
|
+
}, []);
|
|
274
401
|
const patchRow = (projectPath, patch) => {
|
|
275
402
|
setRows(
|
|
276
403
|
(prev) => prev.map((r) => r.project.path === projectPath ? { ...r, ...patch } : r)
|
|
@@ -343,10 +470,11 @@ function App({ rootDir, targets }) {
|
|
|
343
470
|
else if (key.return && selected.length > 0) setMode("confirm");
|
|
344
471
|
});
|
|
345
472
|
const listHeight = Math.max(5, (stdout?.rows ?? 24) - 8);
|
|
473
|
+
const rowWidth = Math.max(20, (stdout?.columns ?? 80) - 1);
|
|
346
474
|
const offset = Math.max(0, Math.min(cursor - Math.floor(listHeight / 2), visible.length - listHeight));
|
|
347
475
|
const windowRows = visible.slice(offset, offset + listHeight);
|
|
348
476
|
const totalFound = rows.reduce((a, r) => a + (r.size ?? 0), 0);
|
|
349
|
-
const displayDir =
|
|
477
|
+
const displayDir = path4.resolve(rootDir).replace(os3.homedir(), "~");
|
|
350
478
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
351
479
|
/* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
|
|
352
480
|
/* @__PURE__ */ jsx2(Text3, { bold: true, color: "cyan", children: "sweeply" }),
|
|
@@ -371,7 +499,15 @@ function App({ rootDir, targets }) {
|
|
|
371
499
|
rootDir,
|
|
372
500
|
"."
|
|
373
501
|
] }),
|
|
374
|
-
windowRows.map((row, i) => /* @__PURE__ */ jsx2(
|
|
502
|
+
windowRows.map((row, i) => /* @__PURE__ */ jsx2(
|
|
503
|
+
ProjectRow,
|
|
504
|
+
{
|
|
505
|
+
row,
|
|
506
|
+
isCursor: offset + i === cursor,
|
|
507
|
+
width: rowWidth
|
|
508
|
+
},
|
|
509
|
+
row.project.path
|
|
510
|
+
)),
|
|
375
511
|
showDetail && current?.git && /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, marginTop: 1, children: [
|
|
376
512
|
/* @__PURE__ */ jsx2(Text3, { bold: true, children: current.project.path }),
|
|
377
513
|
current.git.detail.split("\n").map((line, i) => /* @__PURE__ */ jsx2(Text3, { color: "gray", children: line }, i))
|
|
@@ -402,6 +538,15 @@ function App({ rootDir, targets }) {
|
|
|
402
538
|
"borrando\u2026 ",
|
|
403
539
|
deleteProgress
|
|
404
540
|
] }) }),
|
|
541
|
+
update && /* @__PURE__ */ jsx2(Box3, { marginTop: 1, children: /* @__PURE__ */ jsxs3(Text3, { color: "yellow", children: [
|
|
542
|
+
"Hay una versi\xF3n nueva (",
|
|
543
|
+
update.current,
|
|
544
|
+
" \u2192 ",
|
|
545
|
+
update.latest,
|
|
546
|
+
"). Corre:",
|
|
547
|
+
" ",
|
|
548
|
+
/* @__PURE__ */ jsx2(Text3, { bold: true, children: update.command })
|
|
549
|
+
] }) }),
|
|
405
550
|
/* @__PURE__ */ jsx2(
|
|
406
551
|
Footer,
|
|
407
552
|
{
|
|
@@ -436,7 +581,7 @@ function buildTargets(extra) {
|
|
|
436
581
|
// src/cli.tsx
|
|
437
582
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
438
583
|
function parseArgs(argv) {
|
|
439
|
-
let dir2 =
|
|
584
|
+
let dir2 = os4.homedir();
|
|
440
585
|
let extraTargets2 = [];
|
|
441
586
|
for (let i = 0; i < argv.length; i++) {
|
|
442
587
|
const arg = argv[i];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sweeply",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "TUI para encontrar y borrar peso muerto de tus proyectos (node_modules, .next, venv...) con semáforo de seguridad git",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Fora (Ángel Iván López Delgado)",
|