ripencli 0.3.1 → 0.3.3

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 (3) hide show
  1. package/README.md +1 -0
  2. package/dist/cli.js +152 -50
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -73,6 +73,7 @@ Press `s` to open the settings screen. Settings are persisted at `~/.config/ripe
73
73
  | Setting | Default | Description |
74
74
  | ---------------------------- | ------- | ------------------------------------------------------------ |
75
75
  | Sort by update frequency | Off | Packages you update often appear at the top |
76
+ | Separate dev dependencies | On | Show dependencies and devDependencies in separate groups |
76
77
  | Enable scope grouping | Off | Group scoped packages under their scope prefix |
77
78
  | Show grouped scopes on top | Off | Grouped scopes appear before ungrouped packages |
78
79
  | Grouped scopes | — | List of scopes to group (e.g. `@heroui`, `@radix-ui`) |
package/dist/cli.js CHANGED
@@ -535,7 +535,8 @@ const DEFAULT_CONFIG = {
535
535
  groupByScope: false,
536
536
  groupScopes: [],
537
537
  groupsOnTop: false,
538
- frequencySort: false
538
+ frequencySort: false,
539
+ separateDevDeps: true
539
540
  };
540
541
  const CONFIG_DIR = join(homedir(), ".config", "ripen");
541
542
  const CONFIG_PATH = join(CONFIG_DIR, "config.json");
@@ -596,30 +597,37 @@ const GROUP_CHROME = 5;
596
597
  function getScope(name) {
597
598
  return name.match(/^(@[^/]+)\//)?.[1] ?? null;
598
599
  }
599
- function buildDisplayRows(packages, groupByScope = false, groupScopes = [], groupsOnTop = false, frequencySort = false, frequency = {}) {
600
+ /** Strip leading @ for natural alphabetical sorting (so @vercel/x sorts among 'v', not before 'a') */
601
+ function sortableName(name) {
602
+ return name.startsWith("@") ? name.slice(1) : name;
603
+ }
604
+ function buildDisplayRows(packages, groupByScope = false, groupScopes = [], groupsOnTop = false, frequencySort = false, frequency = {}, separateDevDeps = true) {
600
605
  const grouped = /* @__PURE__ */ new Map();
601
606
  packages.forEach((pkg, i) => {
602
- if (!grouped.has(pkg.type)) grouped.set(pkg.type, []);
603
- grouped.get(pkg.type).push({
607
+ const type = !separateDevDeps && pkg.type === "devDependencies" ? "dependencies" : pkg.type;
608
+ if (!grouped.has(type)) grouped.set(type, []);
609
+ grouped.get(type).push({
604
610
  pkg,
605
611
  index: i
606
612
  });
607
613
  });
614
+ const nameSort = (a, b) => sortableName(a.pkg.name).localeCompare(sortableName(b.pkg.name));
608
615
  const freqSort = (a, b) => {
609
616
  const fa = frequency[a.pkg.name] ?? 0;
610
617
  const fb = frequency[b.pkg.name] ?? 0;
611
618
  if (fb !== fa) return fb - fa;
612
- return a.pkg.name.localeCompare(b.pkg.name);
619
+ return sortableName(a.pkg.name).localeCompare(sortableName(b.pkg.name));
613
620
  };
614
621
  const rows = [];
615
622
  for (const type of GROUP_ORDER) {
616
623
  const items = grouped.get(type);
617
624
  if (!items || items.length === 0) continue;
618
625
  const allPkgs = items.map((i) => i.pkg);
626
+ const label = !separateDevDeps && type === "dependencies" ? "All Dependencies" : GROUP_LABELS[type] ?? type;
619
627
  rows.push({
620
628
  kind: "header",
621
629
  groupType: type,
622
- label: GROUP_LABELS[type] ?? type,
630
+ label,
623
631
  packages: allPkgs
624
632
  });
625
633
  if (groupByScope && groupScopes.length > 0) {
@@ -666,7 +674,7 @@ function buildDisplayRows(packages, groupByScope = false, groupScopes = [], grou
666
674
  }
667
675
  };
668
676
  const emitUngrouped = () => {
669
- if (frequencySort) ungrouped.sort(freqSort);
677
+ ungrouped.sort(frequencySort ? freqSort : nameSort);
670
678
  for (const item of ungrouped) rows.push({
671
679
  kind: "package",
672
680
  pkg: item.pkg,
@@ -675,44 +683,95 @@ function buildDisplayRows(packages, groupByScope = false, groupScopes = [], grou
675
683
  scopeKey: null
676
684
  });
677
685
  };
678
- if (groupsOnTop || frequencySort) {
686
+ if (groupsOnTop) {
679
687
  emitScopeGroups();
680
688
  emitUngrouped();
681
- } else {
682
- const emittedScopes = /* @__PURE__ */ new Set();
683
- for (const item of items) {
684
- const scope = getScope(item.pkg.name);
685
- const group = scope ? scopeGroups.find((g) => g.scope === scope) : void 0;
686
- if (group) {
687
- if (!emittedScopes.has(scope)) {
688
- emittedScopes.add(scope);
689
- const scopeKey = `${type}::${scope}`;
690
- rows.push({
691
- kind: "scope-header",
692
- groupType: type,
693
- scope,
694
- packageIndices: group.items.map((si) => si.index),
695
- packages: group.items.map((si) => si.pkg)
696
- });
697
- for (const si of group.items) rows.push({
698
- kind: "package",
699
- pkg: si.pkg,
700
- packageIndex: si.index,
701
- indented: true,
702
- scopeKey
703
- });
704
- }
705
- } else rows.push({
689
+ } else if (frequencySort) {
690
+ const slots = [];
691
+ for (const g of scopeGroups) {
692
+ g.items.sort(freqSort);
693
+ slots.push({
694
+ kind: "group",
695
+ group: g,
696
+ freq: Math.max(...g.items.map((i) => frequency[i.pkg.name] ?? 0)),
697
+ sortKey: sortableName(g.scope)
698
+ });
699
+ }
700
+ for (const item of ungrouped) slots.push({
701
+ kind: "single",
702
+ item,
703
+ freq: frequency[item.pkg.name] ?? 0,
704
+ sortKey: sortableName(item.pkg.name)
705
+ });
706
+ slots.sort((a, b) => {
707
+ if (b.freq !== a.freq) return b.freq - a.freq;
708
+ return a.sortKey.localeCompare(b.sortKey);
709
+ });
710
+ for (const slot of slots) if (slot.kind === "group") {
711
+ const scopeKey = `${type}::${slot.group.scope}`;
712
+ rows.push({
713
+ kind: "scope-header",
714
+ groupType: type,
715
+ scope: slot.group.scope,
716
+ packageIndices: slot.group.items.map((si) => si.index),
717
+ packages: slot.group.items.map((si) => si.pkg)
718
+ });
719
+ for (const si of slot.group.items) rows.push({
706
720
  kind: "package",
707
- pkg: item.pkg,
708
- packageIndex: item.index,
709
- indented: false,
710
- scopeKey: null
721
+ pkg: si.pkg,
722
+ packageIndex: si.index,
723
+ indented: true,
724
+ scopeKey
725
+ });
726
+ } else rows.push({
727
+ kind: "package",
728
+ pkg: slot.item.pkg,
729
+ packageIndex: slot.item.index,
730
+ indented: false,
731
+ scopeKey: null
732
+ });
733
+ } else {
734
+ const slots = [];
735
+ for (const g of scopeGroups) {
736
+ g.items.sort(nameSort);
737
+ slots.push({
738
+ kind: "group",
739
+ group: g,
740
+ sortKey: sortableName(g.scope)
711
741
  });
712
742
  }
743
+ for (const item of ungrouped) slots.push({
744
+ kind: "single",
745
+ item,
746
+ sortKey: sortableName(item.pkg.name)
747
+ });
748
+ slots.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
749
+ for (const slot of slots) if (slot.kind === "group") {
750
+ const scopeKey = `${type}::${slot.group.scope}`;
751
+ rows.push({
752
+ kind: "scope-header",
753
+ groupType: type,
754
+ scope: slot.group.scope,
755
+ packageIndices: slot.group.items.map((si) => si.index),
756
+ packages: slot.group.items.map((si) => si.pkg)
757
+ });
758
+ for (const si of slot.group.items) rows.push({
759
+ kind: "package",
760
+ pkg: si.pkg,
761
+ packageIndex: si.index,
762
+ indented: true,
763
+ scopeKey
764
+ });
765
+ } else rows.push({
766
+ kind: "package",
767
+ pkg: slot.item.pkg,
768
+ packageIndex: slot.item.index,
769
+ indented: false,
770
+ scopeKey: null
771
+ });
713
772
  }
714
773
  } else {
715
- const sorted = frequencySort ? [...items].sort(freqSort) : items;
774
+ const sorted = frequencySort ? [...items].sort(freqSort) : [...items].sort(nameSort);
716
775
  for (const item of sorted) rows.push({
717
776
  kind: "package",
718
777
  pkg: item.pkg,
@@ -770,15 +829,16 @@ function computeMaxPerGroup(terminalRows, groupCount) {
770
829
  const perGroup = Math.floor(available / groupCount);
771
830
  return Math.max(3, perGroup);
772
831
  }
773
- function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelectVersion, onViewChangelog, onConfirm, onOpenSettings, groupByScope = false, groupScopes, groupsOnTop = false, frequencySort = false, frequency = {}, isActive = true }) {
832
+ function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelectVersion, onViewChangelog, onConfirm, onOpenSettings, groupByScope = false, groupScopes, groupsOnTop = false, frequencySort = false, frequency = {}, separateDevDeps = true, isActive = true }) {
774
833
  const [focusedIndex, setFocusedIndex] = useState(0);
775
- const allRows = useMemo(() => buildDisplayRows(packages, groupByScope, groupScopes, groupsOnTop, frequencySort, frequency), [
834
+ const allRows = useMemo(() => buildDisplayRows(packages, groupByScope, groupScopes, groupsOnTop, frequencySort, frequency, separateDevDeps), [
776
835
  packages,
777
836
  groupByScope,
778
837
  groupScopes,
779
838
  groupsOnTop,
780
839
  frequencySort,
781
- frequency
840
+ frequency,
841
+ separateDevDeps
782
842
  ]);
783
843
  const allScopeKeys = useMemo(() => {
784
844
  const keys = /* @__PURE__ */ new Set();
@@ -1710,6 +1770,7 @@ function Settings({ config, onConfigChange, onClose }) {
1710
1770
  const scopes = config.groupScopes;
1711
1771
  const rows = [
1712
1772
  { type: "toggle-frequency" },
1773
+ { type: "toggle-separate-dev" },
1713
1774
  { type: "toggle-group" },
1714
1775
  { type: "toggle-groups-top" },
1715
1776
  { type: "list-header" },
@@ -1766,6 +1827,10 @@ function Settings({ config, onConfigChange, onClose }) {
1766
1827
  ...config,
1767
1828
  groupByScope: !config.groupByScope
1768
1829
  });
1830
+ else if (currentRow?.type === "toggle-separate-dev") onConfigChange({
1831
+ ...config,
1832
+ separateDevDeps: !config.separateDevDeps
1833
+ });
1769
1834
  else if (currentRow?.type === "toggle-groups-top") onConfigChange({
1770
1835
  ...config,
1771
1836
  groupsOnTop: !config.groupsOnTop
@@ -1787,6 +1852,7 @@ function Settings({ config, onConfigChange, onClose }) {
1787
1852
  if (key.escape || input === "s") onClose();
1788
1853
  });
1789
1854
  const freqToggleFocused = currentRow?.type === "toggle-frequency";
1855
+ const separateDevFocused = currentRow?.type === "toggle-separate-dev";
1790
1856
  const groupToggleFocused = currentRow?.type === "toggle-group";
1791
1857
  const groupsTopFocused = currentRow?.type === "toggle-groups-top";
1792
1858
  const listHeaderFocused = currentRow?.type === "list-header";
@@ -1841,6 +1907,38 @@ function Settings({ config, onConfigChange, onClose }) {
1841
1907
  })
1842
1908
  })]
1843
1909
  }),
1910
+ /* @__PURE__ */ jsxs(Box, {
1911
+ flexDirection: "column",
1912
+ marginBottom: 1,
1913
+ children: [/* @__PURE__ */ jsxs(Box, {
1914
+ gap: 1,
1915
+ children: [
1916
+ /* @__PURE__ */ jsx(Text, {
1917
+ color: "greenBright",
1918
+ children: separateDevFocused ? ">" : " "
1919
+ }),
1920
+ /* @__PURE__ */ jsxs(Text, {
1921
+ color: config.separateDevDeps ? "greenBright" : "gray",
1922
+ children: [
1923
+ "[",
1924
+ config.separateDevDeps ? "x" : " ",
1925
+ "]"
1926
+ ]
1927
+ }),
1928
+ /* @__PURE__ */ jsx(Text, {
1929
+ bold: separateDevFocused,
1930
+ color: separateDevFocused ? "whiteBright" : "white",
1931
+ children: "Separate dev dependencies"
1932
+ })
1933
+ ]
1934
+ }), /* @__PURE__ */ jsx(Box, {
1935
+ marginLeft: 6,
1936
+ children: /* @__PURE__ */ jsx(Text, {
1937
+ color: "gray",
1938
+ children: "Show dependencies and devDependencies in separate groups"
1939
+ })
1940
+ })]
1941
+ }),
1844
1942
  /* @__PURE__ */ jsxs(Box, {
1845
1943
  flexDirection: "column",
1846
1944
  marginBottom: 1,
@@ -1938,7 +2036,7 @@ function Settings({ config, onConfigChange, onClose }) {
1938
2036
  })]
1939
2037
  }),
1940
2038
  scopes.map((scope, i) => {
1941
- const itemFocused = flatCursor === 4 + i;
2039
+ const itemFocused = flatCursor === 5 + i;
1942
2040
  return /* @__PURE__ */ jsxs(Box, {
1943
2041
  marginLeft: 4,
1944
2042
  gap: 1,
@@ -2103,14 +2201,7 @@ function SelfUpdatePrompt({ currentVersion, latestVersion, updating, error, onUp
2103
2201
  function App({ project, global, version, installManager }) {
2104
2202
  const { exit } = useApp();
2105
2203
  useInput((_input, key) => {
2106
- if (key.ctrl && _input === "c") {
2107
- setScreen("empty");
2108
- setTimeout(() => {
2109
- exit();
2110
- console.log(" \x1B[32mCancelled.\x1B[0m\n");
2111
- process.exit(0);
2112
- }, 200);
2113
- }
2204
+ if (key.ctrl && _input === "c") setScreen("cancelled");
2114
2205
  });
2115
2206
  const [screen, setScreen] = useState("self-update-check");
2116
2207
  const [latestVersion, setLatestVersion] = useState(null);
@@ -2155,6 +2246,15 @@ function App({ project, global, version, installManager }) {
2155
2246
  }, 300);
2156
2247
  return () => clearTimeout(timer);
2157
2248
  }, [screen]);
2249
+ useEffect(() => {
2250
+ if (screen !== "cancelled") return;
2251
+ const timer = setTimeout(() => {
2252
+ exit();
2253
+ console.log(" \x1B[32mCancelled.\x1B[0m\n");
2254
+ process.exit(0);
2255
+ }, 200);
2256
+ return () => clearTimeout(timer);
2257
+ }, [screen]);
2158
2258
  const [fetchStarted, setFetchStarted] = useState(false);
2159
2259
  useEffect(() => {
2160
2260
  if (screen !== "loading" || fetchStarted) return;
@@ -2370,6 +2470,7 @@ function App({ project, global, version, installManager }) {
2370
2470
  ]
2371
2471
  })]
2372
2472
  });
2473
+ if (screen === "cancelled") return /* @__PURE__ */ jsx(Fragment, {});
2373
2474
  if (screen === "empty") return /* @__PURE__ */ jsxs(Box, {
2374
2475
  flexDirection: "column",
2375
2476
  padding: 1,
@@ -2479,6 +2580,7 @@ function App({ project, global, version, installManager }) {
2479
2580
  groupsOnTop: config.groupsOnTop,
2480
2581
  frequencySort: config.frequencySort,
2481
2582
  frequency,
2583
+ separateDevDeps: config.separateDevDeps,
2482
2584
  isActive: isListActive
2483
2585
  })
2484
2586
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ripencli",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Interactive dependency updater for npm, pnpm, and yarn",
5
5
  "license": "MIT",
6
6
  "author": {