ripencli 1.0.0 → 1.0.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.
Files changed (2) hide show
  1. package/dist/cli.js +127 -34
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -141,8 +141,10 @@ async function fetchChangelog(packageName, fromVersion, toVersion) {
141
141
  const ghRes = await fetch(`https://api.github.com/repos/${repo}/releases?per_page=30`, { headers: { Accept: "application/vnd.github+json" } });
142
142
  if (!ghRes.ok) return [];
143
143
  const releases = await ghRes.json();
144
+ const toMajor = parseVersion(toVersion)[0];
144
145
  const filtered = releases.filter((r) => {
145
146
  if (r.draft || r.prerelease) return false;
147
+ if (fromVersion === "") return parseVersion(r.tag_name)[0] === toMajor && compareVersions(r.tag_name, toVersion) <= 0;
146
148
  return compareVersions(r.tag_name, fromVersion) > 0 && compareVersions(r.tag_name, toVersion) <= 0;
147
149
  }).map((r) => ({
148
150
  version: r.tag_name,
@@ -157,7 +159,7 @@ async function fetchChangelog(packageName, fromVersion, toVersion) {
157
159
  url: latest.html_url
158
160
  }];
159
161
  }
160
- return filtered;
162
+ return filtered.sort((a, b) => compareVersions(a.version, b.version));
161
163
  } catch {
162
164
  return [];
163
165
  }
@@ -1432,9 +1434,13 @@ function openInBrowser(url) {
1432
1434
  }
1433
1435
  //#endregion
1434
1436
  //#region src/ui/MarkdownLine.tsx
1435
- function parseInline(raw) {
1437
+ /** Wrap text in an OSC 8 hyperlink so modern terminals make it clickable. */
1438
+ function osc8(text, url) {
1439
+ return `\x1b]8;;${url}\x07${text}\x1b]8;;\x07`;
1440
+ }
1441
+ function parseInline(raw, repoUrl) {
1436
1442
  const segments = [];
1437
- const re = /(\*\*(.+?)\*\*|`([^`]+)`|\[([^\]]+)\]\(([^)]+)\))/g;
1443
+ const re = /(\*\*(.+?)\*\*|\*([^*\n]+)\*|~~([^~\n]+)~~|`([^`]+)`|\[([^\]]*)\]\(([^)]+)\)|<(?:strong|b)>(.+?)<\/(?:strong|b)>|<(?:em|i)>(.+?)<\/(?:em|i)>|<(?:del|s)>(.+?)<\/(?:del|s)>|<(?:code|samp|kbd)>(.+?)<\/(?:code|samp|kbd)>|(?<![/\w&#])#(\d+)|(?<!\w)@([a-zA-Z0-9][a-zA-Z0-9-]*)|(https?:\/\/[^\s)>\]]+))/g;
1438
1444
  let last = 0;
1439
1445
  let m;
1440
1446
  while ((m = re.exec(raw)) !== null) {
@@ -1445,30 +1451,123 @@ function parseInline(raw) {
1445
1451
  });
1446
1452
  else if (m[3] !== void 0) segments.push({
1447
1453
  text: m[3],
1454
+ italic: true
1455
+ });
1456
+ else if (m[4] !== void 0) segments.push({
1457
+ text: m[4],
1458
+ strikethrough: true
1459
+ });
1460
+ else if (m[5] !== void 0) segments.push({
1461
+ text: m[5],
1448
1462
  code: true
1449
1463
  });
1450
- else if (m[4] !== void 0) {
1451
- segments.push({ text: m[4] });
1464
+ else if (m[6] !== void 0) {
1465
+ const linkText = m[6].trim() || m[7];
1452
1466
  segments.push({
1453
- text: ` (${m[5]})`,
1454
- dim: true
1467
+ text: linkText,
1468
+ link: m[7]
1455
1469
  });
1456
- }
1470
+ } else if (m[8] !== void 0) segments.push({
1471
+ text: m[8],
1472
+ bold: true
1473
+ });
1474
+ else if (m[9] !== void 0) segments.push({
1475
+ text: m[9],
1476
+ italic: true
1477
+ });
1478
+ else if (m[10] !== void 0) segments.push({
1479
+ text: m[10],
1480
+ strikethrough: true
1481
+ });
1482
+ else if (m[11] !== void 0) segments.push({
1483
+ text: m[11],
1484
+ code: true
1485
+ });
1486
+ else if (m[12] !== void 0) {
1487
+ const num = m[12];
1488
+ const url = repoUrl ? `${repoUrl}/issues/${num}` : void 0;
1489
+ segments.push(url ? {
1490
+ text: `#${num}`,
1491
+ link: url
1492
+ } : {
1493
+ text: `#${num}`,
1494
+ code: true
1495
+ });
1496
+ } else if (m[13] !== void 0) {
1497
+ const user = m[13];
1498
+ segments.push({
1499
+ text: `@${user}`,
1500
+ link: `https://github.com/${user}`
1501
+ });
1502
+ } else if (m[14] !== void 0) segments.push({
1503
+ text: m[14],
1504
+ link: m[14]
1505
+ });
1457
1506
  last = m.index + m[0].length;
1458
1507
  }
1459
1508
  if (last < raw.length) segments.push({ text: raw.slice(last) });
1460
1509
  return segments;
1461
1510
  }
1462
- function MarkdownLine({ line, baseColor = "white", baseDim = false }) {
1463
- const raw = line;
1464
- const headingMatch = raw.match(/^(#{1,3})\s+(.*)/);
1511
+ function Segment({ s, baseColor }) {
1512
+ if (s.link) return /* @__PURE__ */ jsx(Text, {
1513
+ color: "blueBright",
1514
+ underline: true,
1515
+ children: osc8(s.text, s.link)
1516
+ });
1517
+ if (s.code) return /* @__PURE__ */ jsx(Text, {
1518
+ color: "cyan",
1519
+ children: s.text
1520
+ });
1521
+ if (s.bold) return /* @__PURE__ */ jsx(Text, {
1522
+ bold: true,
1523
+ color: baseColor,
1524
+ children: s.text
1525
+ });
1526
+ if (s.italic) return /* @__PURE__ */ jsx(Text, {
1527
+ italic: true,
1528
+ color: baseColor,
1529
+ children: s.text
1530
+ });
1531
+ if (s.strikethrough) return /* @__PURE__ */ jsx(Text, {
1532
+ strikethrough: true,
1533
+ color: "gray",
1534
+ children: s.text
1535
+ });
1536
+ return /* @__PURE__ */ jsx(Text, {
1537
+ color: baseColor,
1538
+ children: s.text
1539
+ });
1540
+ }
1541
+ const HTML_ENTITIES = {
1542
+ "&nbsp;": " ",
1543
+ "&amp;": "&",
1544
+ "&lt;": "<",
1545
+ "&gt;": ">",
1546
+ "&quot;": "\"",
1547
+ "&apos;": "'",
1548
+ "&mdash;": "—",
1549
+ "&ndash;": "–",
1550
+ "&hellip;": "…",
1551
+ "&laquo;": "«",
1552
+ "&raquo;": "»"
1553
+ };
1554
+ function decodeEntities(s) {
1555
+ return s.replace(/&[a-z]+;/g, (e) => HTML_ENTITIES[e] ?? e).replace(/&#(\d+);/g, (_, n) => String.fromCodePoint(Number(n))).replace(/&#x([0-9a-f]+);/gi, (_, h) => String.fromCodePoint(parseInt(h, 16)));
1556
+ }
1557
+ function MarkdownLine({ line, baseColor = "white", repoUrl }) {
1558
+ const raw = decodeEntities(line);
1559
+ const headingMatch = raw.match(/^(#{1,6})\s+(.*)/);
1465
1560
  if (headingMatch) {
1466
1561
  const level = headingMatch[1].length;
1467
1562
  const text = headingMatch[2];
1563
+ const color = level === 1 ? "whiteBright" : level === 2 ? "cyanBright" : level === 3 ? "cyan" : "gray";
1468
1564
  return /* @__PURE__ */ jsxs(Text, {
1469
- bold: true,
1470
- color: level === 1 ? "whiteBright" : level === 2 ? "white" : "gray",
1471
- children: [" ", text]
1565
+ bold: level <= 3,
1566
+ color,
1567
+ children: [" " + " ".repeat(Math.max(0, level - 3)), parseInline(text, repoUrl).map((s, i) => /* @__PURE__ */ jsx(Segment, {
1568
+ s,
1569
+ baseColor: color
1570
+ }, i))]
1472
1571
  });
1473
1572
  }
1474
1573
  if (raw.startsWith("> ")) return /* @__PURE__ */ jsxs(Text, {
@@ -1479,11 +1578,10 @@ function MarkdownLine({ line, baseColor = "white", baseDim = false }) {
1479
1578
  if (listMatch) {
1480
1579
  const indent = listMatch[1].match(/^\s*/)?.[0].length ?? 0;
1481
1580
  const content = listMatch[2];
1482
- const segments = parseInline(content);
1483
- return /* @__PURE__ */ jsxs(Text, { children: [" " + " ".repeat(indent) + "• ", segments.map((s, i) => /* @__PURE__ */ jsx(Text, {
1484
- bold: s.bold,
1485
- color: s.code ? "cyan" : s.dim ? "gray" : baseColor,
1486
- children: s.text
1581
+ const segments = parseInline(content, repoUrl);
1582
+ return /* @__PURE__ */ jsxs(Text, { children: [" " + " ".repeat(indent) + "• ", segments.map((s, i) => /* @__PURE__ */ jsx(Segment, {
1583
+ s,
1584
+ baseColor
1487
1585
  }, i))] });
1488
1586
  }
1489
1587
  if (/^[-*_]{3,}$/.test(raw.trim())) return /* @__PURE__ */ jsx(Text, {
@@ -1491,10 +1589,9 @@ function MarkdownLine({ line, baseColor = "white", baseDim = false }) {
1491
1589
  children: " ────────────────────"
1492
1590
  });
1493
1591
  if (!raw.trim()) return /* @__PURE__ */ jsx(Text, { children: " " });
1494
- return /* @__PURE__ */ jsxs(Text, { children: [" ", parseInline(raw).map((s, i) => /* @__PURE__ */ jsx(Text, {
1495
- bold: s.bold,
1496
- color: s.code ? "cyan" : s.dim ? "gray" : baseColor,
1497
- children: s.text
1592
+ return /* @__PURE__ */ jsxs(Text, { children: [" ", parseInline(raw, repoUrl).map((s, i) => /* @__PURE__ */ jsx(Segment, {
1593
+ s,
1594
+ baseColor
1498
1595
  }, i))] });
1499
1596
  }
1500
1597
  //#endregion
@@ -1507,10 +1604,11 @@ function ChangelogPanel({ pkg, onClose }) {
1507
1604
  const [activeEntry, setActiveEntry] = useState(0);
1508
1605
  const scrollRef = useRef(null);
1509
1606
  const { stdout } = useStdout();
1607
+ const isUpToDate = pkg.current === (pkg.targetVersion ?? pkg.latest);
1510
1608
  useEffect(() => {
1511
- Promise.all([fetchChangelog(pkg.name, pkg.current, pkg.targetVersion ?? pkg.latest), fetchRepoUrl(pkg.name)]).then(([e, repo]) => {
1609
+ Promise.all([fetchChangelog(pkg.name, isUpToDate ? "" : pkg.current, pkg.targetVersion ?? pkg.latest), fetchRepoUrl(pkg.name)]).then(([e, repo]) => {
1512
1610
  setEntries(e);
1513
- setActiveEntry(0);
1611
+ setActiveEntry(isUpToDate ? Math.max(0, e.length - 1) : 0);
1514
1612
  setRepoUrl(repo);
1515
1613
  setLoading(false);
1516
1614
  });
@@ -1597,14 +1695,6 @@ function ChangelogPanel({ pkg, onClose }) {
1597
1695
  })
1598
1696
  ]
1599
1697
  }),
1600
- repoUrl && /* @__PURE__ */ jsxs(Text, {
1601
- color: "gray",
1602
- children: [
1603
- " ",
1604
- repoUrl,
1605
- "/releases"
1606
- ]
1607
- }),
1608
1698
  /* @__PURE__ */ jsx(Text, {
1609
1699
  color: "gray",
1610
1700
  children: "────────────────────────────────────────────────────"
@@ -1674,7 +1764,10 @@ function ChangelogPanel({ pkg, onClose }) {
1674
1764
  ref: scrollRef,
1675
1765
  children: /* @__PURE__ */ jsx(Box, {
1676
1766
  flexDirection: "column",
1677
- children: currentEntry.body.split("\n").map((line, j) => /* @__PURE__ */ jsx(MarkdownLine, { line }, j))
1767
+ children: currentEntry.body.split("\n").map((line, j) => /* @__PURE__ */ jsx(MarkdownLine, {
1768
+ line,
1769
+ repoUrl
1770
+ }, j))
1678
1771
  })
1679
1772
  })
1680
1773
  }) : null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ripencli",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Interactive dependency updater for npm, pnpm, yarn, and bun",
5
5
  "license": "MIT",
6
6
  "author": {