ripencli 0.2.3 → 0.2.5

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 +215 -49
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -522,7 +522,16 @@ async function updatePackages(manager, packages, cwd, global = false, onLine) {
522
522
  }
523
523
  //#endregion
524
524
  //#region src/config.ts
525
- const DEFAULT_CONFIG = { groupByScope: true };
525
+ const DEFAULT_UNGROUP_SCOPES = ["@types", "@react-types"];
526
+ const DEFAULT_CONFIG = {
527
+ groupByScope: true,
528
+ addedScopes: [],
529
+ removedDefaults: []
530
+ };
531
+ /** Compute the effective ungroup scopes list: (defaults - removedDefaults) + addedScopes */
532
+ function getEffectiveUngroupScopes(config) {
533
+ return [...DEFAULT_UNGROUP_SCOPES.filter((s) => !config.removedDefaults.includes(s)), ...config.addedScopes];
534
+ }
526
535
  const CONFIG_DIR = join(homedir(), ".config", "ripen");
527
536
  const CONFIG_PATH = join(CONFIG_DIR, "config.json");
528
537
  function loadConfig() {
@@ -565,7 +574,7 @@ const GROUP_CHROME = 5;
565
574
  function getScope(name) {
566
575
  return name.match(/^(@[^/]+)\//)?.[1] ?? null;
567
576
  }
568
- function buildDisplayRows(packages, groupByScope) {
577
+ function buildDisplayRows(packages, groupByScope, ungroupScopes = []) {
569
578
  const grouped = /* @__PURE__ */ new Map();
570
579
  packages.forEach((pkg, i) => {
571
580
  if (!grouped.has(pkg.type)) grouped.set(pkg.type, []);
@@ -590,7 +599,7 @@ function buildDisplayRows(packages, groupByScope) {
590
599
  const unscoped = [];
591
600
  for (const item of items) {
592
601
  const scope = getScope(item.pkg.name);
593
- if (scope) {
602
+ if (scope && !ungroupScopes.includes(scope)) {
594
603
  if (!scopeMap.has(scope)) scopeMap.set(scope, []);
595
604
  scopeMap.get(scope).push(item);
596
605
  } else unscoped.push(item);
@@ -598,7 +607,7 @@ function buildDisplayRows(packages, groupByScope) {
598
607
  const emittedScopes = /* @__PURE__ */ new Set();
599
608
  for (const item of items) {
600
609
  const scope = getScope(item.pkg.name);
601
- if (scope && scopeMap.get(scope).length >= 2) {
610
+ if (scope && scopeMap.has(scope) && scopeMap.get(scope).length >= 2) {
602
611
  if (!emittedScopes.has(scope)) {
603
612
  emittedScopes.add(scope);
604
613
  const scopeItems = scopeMap.get(scope);
@@ -682,9 +691,13 @@ function computeMaxPerGroup(terminalRows, groupCount) {
682
691
  const perGroup = Math.floor(available / groupCount);
683
692
  return Math.max(3, perGroup);
684
693
  }
685
- function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelectVersion, onViewChangelog, onConfirm, onOpenSettings, groupByScope, isActive = true }) {
694
+ function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelectVersion, onViewChangelog, onConfirm, onOpenSettings, groupByScope, ungroupScopes, isActive = true }) {
686
695
  const [focusedIndex, setFocusedIndex] = useState(0);
687
- const allRows = useMemo(() => buildDisplayRows(packages, groupByScope), [packages, groupByScope]);
696
+ const allRows = useMemo(() => buildDisplayRows(packages, groupByScope, ungroupScopes), [
697
+ packages,
698
+ groupByScope,
699
+ ungroupScopes
700
+ ]);
688
701
  const allScopeKeys = useMemo(() => {
689
702
  const keys = /* @__PURE__ */ new Set();
690
703
  for (const row of allRows) if (row.kind === "scope-header") keys.add(`${row.groupType}::${row.scope}`);
@@ -1609,25 +1622,88 @@ function UpdateResults({ results, onDone }) {
1609
1622
  }
1610
1623
  //#endregion
1611
1624
  //#region src/ui/Settings.tsx
1612
- const SETTINGS = [{
1613
- key: "groupByScope",
1614
- label: "Group packages by scope",
1615
- description: "Sub-group scoped packages (@org/pkg) under their scope prefix"
1616
- }];
1617
1625
  function Settings({ config, onConfigChange, onClose }) {
1618
- const [cursor, setCursor] = useState(0);
1626
+ const [inputMode, setInputMode] = useState(false);
1627
+ const [inputValue, setInputValue] = useState("");
1628
+ const effectiveScopes = getEffectiveUngroupScopes(config);
1629
+ const rows = [
1630
+ { type: "toggle" },
1631
+ { type: "list-header" },
1632
+ ...effectiveScopes.map((_, i) => ({
1633
+ type: "list-item",
1634
+ listItemIndex: i
1635
+ }))
1636
+ ];
1637
+ const [flatCursor, setFlatCursor] = useState(0);
1638
+ const currentRow = rows[flatCursor];
1639
+ const addScope = (value) => {
1640
+ const scope = value.startsWith("@") ? value : `@${value}`;
1641
+ if (effectiveScopes.includes(scope)) return;
1642
+ if (DEFAULT_UNGROUP_SCOPES.includes(scope)) onConfigChange({
1643
+ ...config,
1644
+ removedDefaults: config.removedDefaults.filter((s) => s !== scope)
1645
+ });
1646
+ else onConfigChange({
1647
+ ...config,
1648
+ addedScopes: [...config.addedScopes, scope]
1649
+ });
1650
+ };
1651
+ const removeScope = (scope) => {
1652
+ if (DEFAULT_UNGROUP_SCOPES.includes(scope)) onConfigChange({
1653
+ ...config,
1654
+ removedDefaults: [...config.removedDefaults, scope]
1655
+ });
1656
+ else onConfigChange({
1657
+ ...config,
1658
+ addedScopes: config.addedScopes.filter((s) => s !== scope)
1659
+ });
1660
+ };
1619
1661
  useInput((input, key) => {
1620
- if (key.upArrow) setCursor((c) => Math.max(0, c - 1));
1621
- if (key.downArrow) setCursor((c) => Math.min(SETTINGS.length - 1, c + 1));
1662
+ if (inputMode) {
1663
+ if (key.escape) {
1664
+ setInputMode(false);
1665
+ setInputValue("");
1666
+ return;
1667
+ }
1668
+ if (key.return) {
1669
+ const value = inputValue.trim();
1670
+ if (value) addScope(value);
1671
+ setInputMode(false);
1672
+ setInputValue("");
1673
+ return;
1674
+ }
1675
+ if (key.backspace || key.delete) {
1676
+ setInputValue((v) => v.slice(0, -1));
1677
+ return;
1678
+ }
1679
+ if (input && !key.ctrl && !key.meta) setInputValue((v) => v + input);
1680
+ return;
1681
+ }
1682
+ if (key.upArrow) setFlatCursor((c) => Math.max(0, c - 1));
1683
+ if (key.downArrow) setFlatCursor((c) => Math.min(rows.length - 1, c + 1));
1622
1684
  if (input === " " || key.return) {
1623
- const setting = SETTINGS[cursor];
1624
- onConfigChange({
1685
+ if (currentRow?.type === "toggle") onConfigChange({
1625
1686
  ...config,
1626
- [setting.key]: !config[setting.key]
1687
+ groupByScope: !config.groupByScope
1627
1688
  });
1689
+ else if (currentRow?.type === "list-header") {
1690
+ setInputMode(true);
1691
+ setInputValue("");
1692
+ }
1693
+ }
1694
+ if (input === "a" && !key.ctrl && currentRow?.type === "list-header") {
1695
+ setInputMode(true);
1696
+ setInputValue("");
1697
+ }
1698
+ if ((key.backspace || key.delete) && currentRow?.type === "list-item" && currentRow.listItemIndex !== void 0) {
1699
+ const scope = effectiveScopes[currentRow.listItemIndex];
1700
+ removeScope(scope);
1701
+ if (flatCursor >= rows.length - 1) setFlatCursor(Math.max(0, flatCursor - 1));
1628
1702
  }
1629
1703
  if (key.escape || input === "s") onClose();
1630
1704
  });
1705
+ const toggleFocused = flatCursor === 0;
1706
+ const listHeaderFocused = flatCursor === 1;
1631
1707
  return /* @__PURE__ */ jsxs(Box, {
1632
1708
  flexDirection: "column",
1633
1709
  children: [
@@ -1647,41 +1723,110 @@ function Settings({ config, onConfigChange, onClose }) {
1647
1723
  ]
1648
1724
  })
1649
1725
  }),
1650
- SETTINGS.map((setting, i) => {
1651
- const isFocused = cursor === i;
1652
- const isEnabled = config[setting.key];
1653
- return /* @__PURE__ */ jsxs(Box, {
1654
- flexDirection: "column",
1655
- marginBottom: 1,
1656
- children: [/* @__PURE__ */ jsxs(Box, {
1657
- gap: 1,
1658
- children: [
1659
- /* @__PURE__ */ jsx(Text, {
1726
+ /* @__PURE__ */ jsxs(Box, {
1727
+ flexDirection: "column",
1728
+ marginBottom: 1,
1729
+ children: [/* @__PURE__ */ jsxs(Box, {
1730
+ gap: 1,
1731
+ children: [
1732
+ /* @__PURE__ */ jsx(Text, {
1733
+ color: "greenBright",
1734
+ children: toggleFocused ? ">" : " "
1735
+ }),
1736
+ /* @__PURE__ */ jsxs(Text, {
1737
+ color: config.groupByScope ? "greenBright" : "gray",
1738
+ children: [
1739
+ "[",
1740
+ config.groupByScope ? "x" : " ",
1741
+ "]"
1742
+ ]
1743
+ }),
1744
+ /* @__PURE__ */ jsx(Text, {
1745
+ bold: toggleFocused,
1746
+ color: toggleFocused ? "whiteBright" : "white",
1747
+ children: "Group packages by scope"
1748
+ })
1749
+ ]
1750
+ }), /* @__PURE__ */ jsx(Box, {
1751
+ marginLeft: 6,
1752
+ children: /* @__PURE__ */ jsx(Text, {
1753
+ color: "gray",
1754
+ children: "Sub-group scoped packages (@org/pkg) under their scope prefix"
1755
+ })
1756
+ })]
1757
+ }),
1758
+ /* @__PURE__ */ jsxs(Box, {
1759
+ flexDirection: "column",
1760
+ marginBottom: 1,
1761
+ children: [
1762
+ /* @__PURE__ */ jsxs(Box, {
1763
+ flexDirection: "column",
1764
+ marginBottom: 0,
1765
+ children: [/* @__PURE__ */ jsxs(Box, {
1766
+ gap: 1,
1767
+ children: [/* @__PURE__ */ jsx(Text, {
1660
1768
  color: "greenBright",
1661
- children: isFocused ? "" : " "
1662
- }),
1663
- /* @__PURE__ */ jsxs(Text, {
1664
- color: isEnabled ? "greenBright" : "gray",
1665
- children: [
1666
- "[",
1667
- isEnabled ? "✓" : " ",
1668
- "]"
1669
- ]
1670
- }),
1671
- /* @__PURE__ */ jsx(Text, {
1672
- bold: isFocused,
1673
- color: isFocused ? "whiteBright" : "white",
1674
- children: setting.label
1769
+ children: listHeaderFocused ? ">" : " "
1770
+ }), /* @__PURE__ */ jsx(Text, {
1771
+ bold: listHeaderFocused,
1772
+ color: listHeaderFocused ? "whiteBright" : "white",
1773
+ children: "Excluded scopes from grouping"
1774
+ })]
1775
+ }), /* @__PURE__ */ jsx(Box, {
1776
+ marginLeft: 4,
1777
+ children: /* @__PURE__ */ jsx(Text, {
1778
+ color: "gray",
1779
+ children: "Scopes listed here won't be sub-grouped (type to add, delete to remove)"
1675
1780
  })
1676
- ]
1677
- }), /* @__PURE__ */ jsx(Box, {
1678
- marginLeft: 6,
1781
+ })]
1782
+ }),
1783
+ effectiveScopes.map((scope, i) => {
1784
+ const itemFocused = flatCursor === 2 + i;
1785
+ const isDefault = DEFAULT_UNGROUP_SCOPES.includes(scope);
1786
+ return /* @__PURE__ */ jsxs(Box, {
1787
+ marginLeft: 4,
1788
+ gap: 1,
1789
+ children: [
1790
+ /* @__PURE__ */ jsx(Text, {
1791
+ color: "greenBright",
1792
+ children: itemFocused ? ">" : " "
1793
+ }),
1794
+ /* @__PURE__ */ jsx(Text, {
1795
+ color: itemFocused ? "whiteBright" : "white",
1796
+ children: scope
1797
+ }),
1798
+ isDefault && /* @__PURE__ */ jsx(Text, {
1799
+ color: "gray",
1800
+ children: "(default)"
1801
+ }),
1802
+ itemFocused && /* @__PURE__ */ jsx(Text, {
1803
+ color: "gray",
1804
+ children: " delete to remove"
1805
+ })
1806
+ ]
1807
+ }, scope);
1808
+ }),
1809
+ inputMode && currentRow?.type === "list-header" ? /* @__PURE__ */ jsxs(Box, {
1810
+ marginLeft: 4,
1811
+ gap: 1,
1812
+ children: [/* @__PURE__ */ jsx(Text, {
1813
+ color: "greenBright",
1814
+ children: "+"
1815
+ }), /* @__PURE__ */ jsxs(Text, {
1816
+ color: "cyan",
1817
+ children: [inputValue || "", /* @__PURE__ */ jsx(Text, {
1818
+ color: "gray",
1819
+ children: "|"
1820
+ })]
1821
+ })]
1822
+ }) : listHeaderFocused ? /* @__PURE__ */ jsx(Box, {
1823
+ marginLeft: 4,
1679
1824
  children: /* @__PURE__ */ jsx(Text, {
1680
1825
  color: "gray",
1681
- children: setting.description
1826
+ children: "press enter or a to add"
1682
1827
  })
1683
- })]
1684
- }, setting.key);
1828
+ }) : null
1829
+ ]
1685
1830
  }),
1686
1831
  /* @__PURE__ */ jsx(Box, {
1687
1832
  marginTop: 1,
@@ -1690,7 +1835,7 @@ function Settings({ config, onConfigChange, onClose }) {
1690
1835
  children: [
1691
1836
  /* @__PURE__ */ jsx(Text, {
1692
1837
  color: "white",
1693
- children: "↑↓"
1838
+ children: "arrow keys"
1694
1839
  }),
1695
1840
  " navigate",
1696
1841
  " ",
@@ -1700,6 +1845,18 @@ function Settings({ config, onConfigChange, onClose }) {
1700
1845
  }),
1701
1846
  " toggle",
1702
1847
  " ",
1848
+ /* @__PURE__ */ jsx(Text, {
1849
+ color: "white",
1850
+ children: "a"
1851
+ }),
1852
+ " add",
1853
+ " ",
1854
+ /* @__PURE__ */ jsx(Text, {
1855
+ color: "white",
1856
+ children: "delete"
1857
+ }),
1858
+ " remove",
1859
+ " ",
1703
1860
  /* @__PURE__ */ jsx(Text, {
1704
1861
  color: "white",
1705
1862
  children: "esc"
@@ -1831,7 +1988,15 @@ function App({ project, global, version, installManager }) {
1831
1988
  const timer = setTimeout(() => {
1832
1989
  exit();
1833
1990
  process.exit(0);
1834
- }, 1500);
1991
+ }, 300);
1992
+ return () => clearTimeout(timer);
1993
+ }, [screen]);
1994
+ useEffect(() => {
1995
+ if (screen !== "empty") return;
1996
+ const timer = setTimeout(() => {
1997
+ exit();
1998
+ process.exit(0);
1999
+ }, 300);
1835
2000
  return () => clearTimeout(timer);
1836
2001
  }, [screen]);
1837
2002
  const [fetchStarted, setFetchStarted] = useState(false);
@@ -2148,6 +2313,7 @@ function App({ project, global, version, installManager }) {
2148
2313
  onConfirm: handleConfirm,
2149
2314
  onOpenSettings: () => setScreen("settings"),
2150
2315
  groupByScope: config.groupByScope,
2316
+ ungroupScopes: getEffectiveUngroupScopes(config),
2151
2317
  isActive: isListActive
2152
2318
  })
2153
2319
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ripencli",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Interactive dependency updater for npm, pnpm, and yarn",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -63,6 +63,6 @@
63
63
  "@types/react": "^19.2.14",
64
64
  "prettier": "^3.8.1",
65
65
  "tsdown": "^0.21.4",
66
- "typescript": "^5.5.0"
66
+ "typescript": "^5.9.3"
67
67
  }
68
68
  }