ripencli 1.1.0 → 1.1.1
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 +3 -0
- package/dist/cli.js +146 -95
- package/package.json +2 -2
package/README.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -221,7 +221,7 @@ async function fetchRegistryInfoWithRetry(packageName) {
|
|
|
221
221
|
clearTimeout(timeout);
|
|
222
222
|
if (!res.ok) return null;
|
|
223
223
|
const data = await res.json();
|
|
224
|
-
const version = data["dist-tags"]?.latest ?? null;
|
|
224
|
+
const version = data["dist-tags"]?.latest ?? Object.keys(data.versions ?? {}).at(-1) ?? null;
|
|
225
225
|
if (!version) return null;
|
|
226
226
|
return {
|
|
227
227
|
version,
|
|
@@ -291,6 +291,108 @@ async function getOutdatedPackages(manager, cwd, global = false, onLine, showAll
|
|
|
291
291
|
packages
|
|
292
292
|
};
|
|
293
293
|
}
|
|
294
|
+
/** Fetch publish dates from the registry and attach them to existing packages in-place. */
|
|
295
|
+
async function hydratePublishDates(packages) {
|
|
296
|
+
if (packages.length === 0) return;
|
|
297
|
+
const info = await fetchAllLatest(packages.map((p) => p.name), 8);
|
|
298
|
+
for (const pkg of packages) {
|
|
299
|
+
const r = info.get(pkg.name);
|
|
300
|
+
if (r?.publishedAt) pkg.latestPublishedAt = r.publishedAt;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* List all globally installed packages for a manager.
|
|
305
|
+
* Returns name + currently installed version.
|
|
306
|
+
*/
|
|
307
|
+
async function listGlobalPackages(manager, cwd) {
|
|
308
|
+
try {
|
|
309
|
+
if (manager === "npm") {
|
|
310
|
+
const { stdout } = await execa("npm", [
|
|
311
|
+
"list",
|
|
312
|
+
"-g",
|
|
313
|
+
"--depth=0",
|
|
314
|
+
"--json"
|
|
315
|
+
], {
|
|
316
|
+
cwd,
|
|
317
|
+
reject: false
|
|
318
|
+
});
|
|
319
|
+
const data = JSON.parse(stdout);
|
|
320
|
+
return Object.entries(data.dependencies ?? {}).map(([name, info]) => ({
|
|
321
|
+
name,
|
|
322
|
+
current: info.version ?? "N/A"
|
|
323
|
+
}));
|
|
324
|
+
}
|
|
325
|
+
if (manager === "pnpm") {
|
|
326
|
+
const { stdout } = await execa("pnpm", [
|
|
327
|
+
"list",
|
|
328
|
+
"-g",
|
|
329
|
+
"--json"
|
|
330
|
+
], {
|
|
331
|
+
cwd,
|
|
332
|
+
reject: false
|
|
333
|
+
});
|
|
334
|
+
const data = JSON.parse(stdout);
|
|
335
|
+
const deps = Array.isArray(data) ? data[0]?.dependencies ?? {} : data.dependencies ?? {};
|
|
336
|
+
return Object.entries(deps).map(([name, info]) => ({
|
|
337
|
+
name,
|
|
338
|
+
current: info.version ?? "N/A"
|
|
339
|
+
}));
|
|
340
|
+
}
|
|
341
|
+
if (manager === "yarn") {
|
|
342
|
+
const { stdout } = await execa("yarn", [
|
|
343
|
+
"global",
|
|
344
|
+
"list",
|
|
345
|
+
"--depth=0",
|
|
346
|
+
"--json"
|
|
347
|
+
], {
|
|
348
|
+
cwd,
|
|
349
|
+
reject: false
|
|
350
|
+
});
|
|
351
|
+
const pkgs = [];
|
|
352
|
+
for (const line of stdout.trim().split("\n")) try {
|
|
353
|
+
const obj = JSON.parse(line);
|
|
354
|
+
if (obj.type === "tree" && obj.data?.trees) for (const tree of obj.data.trees) {
|
|
355
|
+
const match = tree.name?.match(/^(.+)@([^@]+)$/);
|
|
356
|
+
if (match) pkgs.push({
|
|
357
|
+
name: match[1],
|
|
358
|
+
current: match[2]
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
} catch {}
|
|
362
|
+
return pkgs;
|
|
363
|
+
}
|
|
364
|
+
} catch {}
|
|
365
|
+
return [];
|
|
366
|
+
}
|
|
367
|
+
/** Global mode, showAll=true: list all installed packages then check registry for latest. */
|
|
368
|
+
async function getGlobalAllPackages(manager, cwd, onLine) {
|
|
369
|
+
const installed = await listGlobalPackages(manager, cwd);
|
|
370
|
+
if (installed.length === 0) return {
|
|
371
|
+
ok: true,
|
|
372
|
+
packages: []
|
|
373
|
+
};
|
|
374
|
+
const registryInfo = await fetchAllLatest(installed.map((p) => p.name), 8, onLine);
|
|
375
|
+
const packages = [];
|
|
376
|
+
for (const dep of installed) {
|
|
377
|
+
const info = registryInfo.get(dep.name);
|
|
378
|
+
if (!info) continue;
|
|
379
|
+
packages.push({
|
|
380
|
+
name: dep.name,
|
|
381
|
+
current: dep.current,
|
|
382
|
+
wanted: info.version,
|
|
383
|
+
latest: info.version,
|
|
384
|
+
dependent: "",
|
|
385
|
+
type: "global",
|
|
386
|
+
selected: false,
|
|
387
|
+
targetVersion: info.version,
|
|
388
|
+
latestPublishedAt: info.publishedAt || void 0
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
ok: true,
|
|
393
|
+
packages
|
|
394
|
+
};
|
|
395
|
+
}
|
|
294
396
|
/** Global mode: shell out to manager's outdated command */
|
|
295
397
|
async function getGlobalOutdatedPackages(manager, cwd, onLine) {
|
|
296
398
|
const args = [
|
|
@@ -336,34 +438,36 @@ async function getGlobalOutdatedPackages(manager, cwd, onLine) {
|
|
|
336
438
|
ok: true,
|
|
337
439
|
packages: []
|
|
338
440
|
};
|
|
441
|
+
let packages;
|
|
339
442
|
if (manager === "yarn") try {
|
|
340
|
-
|
|
341
|
-
ok: true,
|
|
342
|
-
packages: parseYarnOutdated(raw, true)
|
|
343
|
-
};
|
|
443
|
+
packages = parseYarnOutdated(raw, true);
|
|
344
444
|
} catch {
|
|
345
445
|
return {
|
|
346
446
|
ok: false,
|
|
347
447
|
error: "Failed to parse yarn outdated output. Try again."
|
|
348
448
|
};
|
|
349
449
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
error: stderr.trim() || raw.slice(0, 120)
|
|
354
|
-
};
|
|
355
|
-
try {
|
|
356
|
-
const data = JSON.parse(jsonStr);
|
|
357
|
-
return {
|
|
358
|
-
ok: true,
|
|
359
|
-
packages: manager === "pnpm" ? parsePnpmOutdated(data) : parseNpmOutdated(data)
|
|
360
|
-
};
|
|
361
|
-
} catch {
|
|
362
|
-
return {
|
|
450
|
+
else {
|
|
451
|
+
const jsonStr = extractJson(raw);
|
|
452
|
+
if (!jsonStr) return {
|
|
363
453
|
ok: false,
|
|
364
|
-
error:
|
|
454
|
+
error: stderr.trim() || raw.slice(0, 120)
|
|
365
455
|
};
|
|
456
|
+
try {
|
|
457
|
+
const data = JSON.parse(jsonStr);
|
|
458
|
+
packages = manager === "pnpm" ? parsePnpmOutdated(data) : parseNpmOutdated(data);
|
|
459
|
+
} catch {
|
|
460
|
+
return {
|
|
461
|
+
ok: false,
|
|
462
|
+
error: "Failed to parse outdated output. Try again."
|
|
463
|
+
};
|
|
464
|
+
}
|
|
366
465
|
}
|
|
466
|
+
await hydratePublishDates(packages);
|
|
467
|
+
return {
|
|
468
|
+
ok: true,
|
|
469
|
+
packages
|
|
470
|
+
};
|
|
367
471
|
}
|
|
368
472
|
/**
|
|
369
473
|
* Extract the first top-level JSON object from a string that may contain
|
|
@@ -419,15 +523,16 @@ const ALL_MANAGERS = [
|
|
|
419
523
|
"yarn"
|
|
420
524
|
];
|
|
421
525
|
/**
|
|
422
|
-
* Check all available package managers for global
|
|
526
|
+
* Check all available package managers for global packages in parallel.
|
|
423
527
|
* Each returned package is tagged with its owning manager.
|
|
528
|
+
* showAll=true lists every installed package, not just outdated ones.
|
|
424
529
|
*/
|
|
425
|
-
async function getAllGlobalOutdated(cwd, onLine) {
|
|
530
|
+
async function getAllGlobalOutdated(cwd, onLine, showAll = false) {
|
|
426
531
|
const managers = (await Promise.all(ALL_MANAGERS.map(async (m) => ({
|
|
427
532
|
manager: m,
|
|
428
533
|
ok: await isManagerAvailable(m)
|
|
429
534
|
})))).filter((a) => a.ok).map((a) => a.manager);
|
|
430
|
-
const results = await Promise.all(managers.map((m) => getOutdatedPackages(m, cwd, true, onLine)));
|
|
535
|
+
const results = await Promise.all(managers.map((m) => showAll ? getGlobalAllPackages(m, cwd, onLine) : getOutdatedPackages(m, cwd, true, onLine)));
|
|
431
536
|
const allPackages = [];
|
|
432
537
|
for (let i = 0; i < managers.length; i++) {
|
|
433
538
|
const result = results[i];
|
|
@@ -2111,10 +2216,9 @@ function Settings({ config, onConfigChange, onClose }) {
|
|
|
2111
2216
|
}
|
|
2112
2217
|
//#endregion
|
|
2113
2218
|
//#region src/ui/SelfUpdatePrompt.tsx
|
|
2114
|
-
function SelfUpdatePrompt({ currentVersion, latestVersion,
|
|
2219
|
+
function SelfUpdatePrompt({ currentVersion, latestVersion, onUpdate, onSkip }) {
|
|
2115
2220
|
const [selected, setSelected] = useState(0);
|
|
2116
|
-
useInput((
|
|
2117
|
-
if (updating) return;
|
|
2221
|
+
useInput((_input, key) => {
|
|
2118
2222
|
if (key.upArrow) setSelected(0);
|
|
2119
2223
|
if (key.downArrow) setSelected(1);
|
|
2120
2224
|
if (key.return) if (selected === 0) onUpdate();
|
|
@@ -2131,9 +2235,9 @@ function SelfUpdatePrompt({ currentVersion, latestVersion, updating, error, onUp
|
|
|
2131
2235
|
}),
|
|
2132
2236
|
/* @__PURE__ */ jsx(Box, {
|
|
2133
2237
|
marginTop: 1,
|
|
2134
|
-
flexDirection: "column",
|
|
2135
2238
|
children: /* @__PURE__ */ jsxs(Text, { children: [
|
|
2136
|
-
"A new version is available!
|
|
2239
|
+
"A new version is available!",
|
|
2240
|
+
" ",
|
|
2137
2241
|
/* @__PURE__ */ jsx(Text, {
|
|
2138
2242
|
color: "gray",
|
|
2139
2243
|
children: currentVersion
|
|
@@ -2148,23 +2252,7 @@ function SelfUpdatePrompt({ currentVersion, latestVersion, updating, error, onUp
|
|
|
2148
2252
|
})
|
|
2149
2253
|
] })
|
|
2150
2254
|
}),
|
|
2151
|
-
|
|
2152
|
-
marginTop: 1,
|
|
2153
|
-
children: [/* @__PURE__ */ jsxs(Text, {
|
|
2154
|
-
color: "red",
|
|
2155
|
-
children: ["✗ Update failed: ", error]
|
|
2156
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
2157
|
-
color: "gray",
|
|
2158
|
-
children: " Continuing…"
|
|
2159
|
-
})]
|
|
2160
|
-
}),
|
|
2161
|
-
updating ? /* @__PURE__ */ jsx(Box, {
|
|
2162
|
-
marginTop: 1,
|
|
2163
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
2164
|
-
color: "gray",
|
|
2165
|
-
children: "Updating ripen…"
|
|
2166
|
-
})
|
|
2167
|
-
}) : /* @__PURE__ */ jsxs(Box, {
|
|
2255
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
2168
2256
|
marginTop: 1,
|
|
2169
2257
|
flexDirection: "column",
|
|
2170
2258
|
children: [/* @__PURE__ */ jsxs(Text, { children: [selected === 0 ? /* @__PURE__ */ jsx(Text, {
|
|
@@ -2172,7 +2260,7 @@ function SelfUpdatePrompt({ currentVersion, latestVersion, updating, error, onUp
|
|
|
2172
2260
|
children: "❯ "
|
|
2173
2261
|
}) : /* @__PURE__ */ jsx(Text, { children: " " }), /* @__PURE__ */ jsx(Text, {
|
|
2174
2262
|
color: selected === 0 ? "white" : "gray",
|
|
2175
|
-
children: "Update"
|
|
2263
|
+
children: "Update (copies command to clipboard)"
|
|
2176
2264
|
})] }), /* @__PURE__ */ jsxs(Text, { children: [selected === 1 ? /* @__PURE__ */ jsx(Text, {
|
|
2177
2265
|
color: "greenBright",
|
|
2178
2266
|
children: "❯ "
|
|
@@ -2233,8 +2321,6 @@ function TerminalOutputBox({ message, command, outputLines, maxLines }) {
|
|
|
2233
2321
|
//#region src/ui/hooks/use-self-update.ts
|
|
2234
2322
|
function useSelfUpdate(currentVersion, installManager) {
|
|
2235
2323
|
const [latestVersion, setLatestVersion] = useState(null);
|
|
2236
|
-
const [selfUpdating, setSelfUpdating] = useState(false);
|
|
2237
|
-
const [selfUpdateError, setSelfUpdateError] = useState(null);
|
|
2238
2324
|
const [checkComplete, setCheckComplete] = useState(false);
|
|
2239
2325
|
const [hasUpdate, setHasUpdate] = useState(false);
|
|
2240
2326
|
useEffect(() => {
|
|
@@ -2251,33 +2337,17 @@ function useSelfUpdate(currentVersion, installManager) {
|
|
|
2251
2337
|
cancelled = true;
|
|
2252
2338
|
};
|
|
2253
2339
|
}, []);
|
|
2254
|
-
const
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
"add",
|
|
2260
|
-
`ripencli@${latestVersion}`
|
|
2261
|
-
] : [
|
|
2262
|
-
"add",
|
|
2263
|
-
"--global",
|
|
2264
|
-
`ripencli@${latestVersion}`
|
|
2265
|
-
]);
|
|
2266
|
-
setSelfUpdating(false);
|
|
2267
|
-
return true;
|
|
2268
|
-
} catch (err) {
|
|
2269
|
-
setSelfUpdateError(err.message ?? "Unknown error");
|
|
2270
|
-
setSelfUpdating(false);
|
|
2271
|
-
return false;
|
|
2272
|
-
}
|
|
2340
|
+
const buildUpdateCommand = () => {
|
|
2341
|
+
const version = latestVersion ?? "latest";
|
|
2342
|
+
if (installManager === "yarn") return `yarn global add ripencli@${version}`;
|
|
2343
|
+
if (installManager === "bun") return `bun add -g ripencli@${version}`;
|
|
2344
|
+
return `${installManager} add -g ripencli@${version}`;
|
|
2273
2345
|
};
|
|
2274
2346
|
return {
|
|
2275
2347
|
latestVersion,
|
|
2276
|
-
selfUpdating,
|
|
2277
|
-
selfUpdateError,
|
|
2278
2348
|
checkComplete,
|
|
2279
2349
|
hasUpdate,
|
|
2280
|
-
|
|
2350
|
+
buildUpdateCommand
|
|
2281
2351
|
};
|
|
2282
2352
|
}
|
|
2283
2353
|
//#endregion
|
|
@@ -2367,7 +2437,7 @@ function App({ project, global, showAll, version, installManager, onCancelled, o
|
|
|
2367
2437
|
useInput((_input, key) => {
|
|
2368
2438
|
if (key.ctrl && _input === "c") setScreen("cancelled");
|
|
2369
2439
|
});
|
|
2370
|
-
useExitOnScreen(screen, ["
|
|
2440
|
+
useExitOnScreen(screen, ["empty"], exit);
|
|
2371
2441
|
useExitOnScreen(screen, ["cancelled"], exit, {
|
|
2372
2442
|
delay: 200,
|
|
2373
2443
|
beforeExit: () => onCancelled?.()
|
|
@@ -2377,9 +2447,11 @@ function App({ project, global, showAll, version, installManager, onCancelled, o
|
|
|
2377
2447
|
if (screen !== "self-update-check") return;
|
|
2378
2448
|
setScreen(selfUpdate.hasUpdate ? "self-update" : "loading");
|
|
2379
2449
|
}, [selfUpdate.checkComplete]);
|
|
2380
|
-
const handleSelfUpdate =
|
|
2381
|
-
|
|
2382
|
-
|
|
2450
|
+
const handleSelfUpdate = () => {
|
|
2451
|
+
const cmd = selfUpdate.buildUpdateCommand();
|
|
2452
|
+
copyToClipboard(cmd);
|
|
2453
|
+
onCopied?.([cmd]);
|
|
2454
|
+
exit();
|
|
2383
2455
|
};
|
|
2384
2456
|
const [fetchStarted, setFetchStarted] = useState(false);
|
|
2385
2457
|
useEffect(() => {
|
|
@@ -2387,7 +2459,7 @@ function App({ project, global, showAll, version, installManager, onCancelled, o
|
|
|
2387
2459
|
setFetchStarted(true);
|
|
2388
2460
|
terminal.setLoadingMsg("Checking for outdated packages…");
|
|
2389
2461
|
terminal.setTerminalCmd(global ? "Checking all package managers…" : "Checking npm registry…");
|
|
2390
|
-
(global ? getAllGlobalOutdated(project.cwd, terminal.onLine) : getOutdatedPackages(project.manager, project.cwd, false, terminal.onLine, showAll)).then((result) => {
|
|
2462
|
+
(global ? getAllGlobalOutdated(project.cwd, terminal.onLine, showAll) : getOutdatedPackages(project.manager, project.cwd, false, terminal.onLine, showAll)).then((result) => {
|
|
2391
2463
|
if (!result.ok) {
|
|
2392
2464
|
setErrorMsg(result.error);
|
|
2393
2465
|
setScreen("error");
|
|
@@ -2433,30 +2505,9 @@ function App({ project, global, showAll, version, installManager, onCancelled, o
|
|
|
2433
2505
|
if (screen === "self-update") return /* @__PURE__ */ jsx(SelfUpdatePrompt, {
|
|
2434
2506
|
currentVersion: version,
|
|
2435
2507
|
latestVersion: selfUpdate.latestVersion,
|
|
2436
|
-
updating: selfUpdate.selfUpdating,
|
|
2437
|
-
error: selfUpdate.selfUpdateError,
|
|
2438
2508
|
onUpdate: handleSelfUpdate,
|
|
2439
2509
|
onSkip: () => setScreen("loading")
|
|
2440
2510
|
});
|
|
2441
|
-
if (screen === "self-update-done") return /* @__PURE__ */ jsxs(Box, {
|
|
2442
|
-
flexDirection: "column",
|
|
2443
|
-
padding: 1,
|
|
2444
|
-
children: [/* @__PURE__ */ jsxs(Text, {
|
|
2445
|
-
color: "greenBright",
|
|
2446
|
-
bold: true,
|
|
2447
|
-
children: [" ", "ripen"]
|
|
2448
|
-
}), /* @__PURE__ */ jsx(Box, {
|
|
2449
|
-
marginTop: 1,
|
|
2450
|
-
children: /* @__PURE__ */ jsxs(Text, {
|
|
2451
|
-
color: "green",
|
|
2452
|
-
children: [
|
|
2453
|
-
"✓ Updated to v",
|
|
2454
|
-
selfUpdate.latestVersion,
|
|
2455
|
-
". Run ripen again to use the new version."
|
|
2456
|
-
]
|
|
2457
|
-
})
|
|
2458
|
-
})]
|
|
2459
|
-
});
|
|
2460
2511
|
if (screen === "loading") return /* @__PURE__ */ jsx(TerminalOutputBox, {
|
|
2461
2512
|
message: terminal.loadingMsg,
|
|
2462
2513
|
command: terminal.terminalCmd,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ripencli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Interactive dependency updater for npm, pnpm, yarn, and bun",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"type": "module",
|
|
30
30
|
"bin": {
|
|
31
|
-
"ripen": "
|
|
31
|
+
"ripen": "dist/cli.js"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsdown",
|