vue-hook-optimizer 0.0.80 → 0.0.82

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/index.cjs CHANGED
@@ -1689,6 +1689,184 @@ function getMermaidText(graph, nodesUsedInTemplate, nodesUsedInStyle = /* @__PUR
1689
1689
  return mermaidText;
1690
1690
  }
1691
1691
 
1692
+ //#endregion
1693
+ //#region src/suggest/community.ts
1694
+ function buildUndirectedGraph(graph) {
1695
+ const undirected = /* @__PURE__ */ new Map();
1696
+ for (const [node, edges] of graph) {
1697
+ if (!undirected.has(node)) undirected.set(node, /* @__PURE__ */ new Set());
1698
+ for (const edge of edges) {
1699
+ undirected.get(node).add(edge.node);
1700
+ if (!undirected.has(edge.node)) undirected.set(edge.node, /* @__PURE__ */ new Set());
1701
+ undirected.get(edge.node).add(node);
1702
+ }
1703
+ }
1704
+ return undirected;
1705
+ }
1706
+ function createSeededRandom(seed) {
1707
+ if (seed === void 0) return Math.random;
1708
+ let state = seed;
1709
+ return () => {
1710
+ state = state * 1103515245 + 12345 & 2147483647;
1711
+ return state / 2147483647;
1712
+ };
1713
+ }
1714
+ function shuffleArray(array, random = Math.random) {
1715
+ const result = [...array];
1716
+ for (let i = result.length - 1; i > 0; i--) {
1717
+ const j = Math.floor(random() * (i + 1));
1718
+ [result[i], result[j]] = [result[j], result[i]];
1719
+ }
1720
+ return result;
1721
+ }
1722
+ /**
1723
+ * Label Propagation Algorithm for community detection.
1724
+ *
1725
+ * Each node starts with its own unique label. In each iteration,
1726
+ * nodes adopt the most frequent label among their neighbors.
1727
+ * The algorithm converges when labels no longer change.
1728
+ *
1729
+ * This helps identify groups of nodes that are tightly connected
1730
+ * and could potentially be extracted into separate hooks.
1731
+ */
1732
+ function detectCommunities(graph, options = {}) {
1733
+ const { maxIterations = 100 } = options;
1734
+ const random = createSeededRandom(options.seed);
1735
+ const undirectedGraph = buildUndirectedGraph(graph);
1736
+ const nodes = Array.from(undirectedGraph.keys());
1737
+ if (nodes.length === 0) return {
1738
+ communities: [],
1739
+ nodeToCommuntiy: /* @__PURE__ */ new Map()
1740
+ };
1741
+ const labels = /* @__PURE__ */ new Map();
1742
+ nodes.forEach((node, index) => {
1743
+ labels.set(node, index);
1744
+ });
1745
+ let changed = true;
1746
+ let iterations = 0;
1747
+ while (changed && iterations < maxIterations) {
1748
+ changed = false;
1749
+ iterations++;
1750
+ const shuffledNodes = shuffleArray(nodes, random);
1751
+ for (const node of shuffledNodes) {
1752
+ const neighbors = undirectedGraph.get(node);
1753
+ if (!neighbors || neighbors.size === 0) continue;
1754
+ const labelCounts = /* @__PURE__ */ new Map();
1755
+ for (const neighbor of neighbors) {
1756
+ const neighborLabel = labels.get(neighbor);
1757
+ labelCounts.set(neighborLabel, (labelCounts.get(neighborLabel) || 0) + 1);
1758
+ }
1759
+ let maxCount = 0;
1760
+ let maxLabels = [];
1761
+ for (const [label, count] of labelCounts) if (count > maxCount) {
1762
+ maxCount = count;
1763
+ maxLabels = [label];
1764
+ } else if (count === maxCount) maxLabels.push(label);
1765
+ const currentLabel = labels.get(node);
1766
+ if (maxLabels.includes(currentLabel)) continue;
1767
+ const newLabel = maxLabels[Math.floor(random() * maxLabels.length)];
1768
+ if (newLabel !== currentLabel) {
1769
+ labels.set(node, newLabel);
1770
+ changed = true;
1771
+ }
1772
+ }
1773
+ }
1774
+ const communityMap = /* @__PURE__ */ new Map();
1775
+ for (const [node, label] of labels) {
1776
+ if (!communityMap.has(label)) communityMap.set(label, /* @__PURE__ */ new Set());
1777
+ communityMap.get(label).add(node);
1778
+ }
1779
+ const communities = [];
1780
+ const nodeToCommuntiy = /* @__PURE__ */ new Map();
1781
+ let communityId = 0;
1782
+ for (const [_, nodeSet] of communityMap) {
1783
+ communities.push({
1784
+ id: communityId,
1785
+ nodes: nodeSet
1786
+ });
1787
+ for (const node of nodeSet) nodeToCommuntiy.set(node, communityId);
1788
+ communityId++;
1789
+ }
1790
+ communities.sort((a, b) => b.nodes.size - a.nodes.size);
1791
+ const reindexedCommunities = [];
1792
+ const newNodeToCommunity = /* @__PURE__ */ new Map();
1793
+ communities.forEach((community, newId) => {
1794
+ reindexedCommunities.push({
1795
+ id: newId,
1796
+ nodes: community.nodes
1797
+ });
1798
+ for (const node of community.nodes) newNodeToCommunity.set(node, newId);
1799
+ });
1800
+ return {
1801
+ communities: reindexedCommunities,
1802
+ nodeToCommuntiy: newNodeToCommunity
1803
+ };
1804
+ }
1805
+ /**
1806
+ * Generate HSL colors for communities.
1807
+ * Uses golden ratio to distribute hues evenly.
1808
+ */
1809
+ function generateCommunityColors(communityCount) {
1810
+ const colors = [];
1811
+ const goldenRatio = .618033988749895;
1812
+ let hue = .1;
1813
+ for (let i = 0; i < communityCount; i++) {
1814
+ hue = (hue + goldenRatio) % 1;
1815
+ const h = Math.floor(hue * 360);
1816
+ const s = 65 + i % 3 * 10;
1817
+ const l = 45 + i % 2 * 10;
1818
+ colors.push(`hsl(${h}, ${s}%, ${l}%)`);
1819
+ }
1820
+ return colors;
1821
+ }
1822
+ /**
1823
+ * Generate RGBA colors for VS Code decorations.
1824
+ * Returns both background (low opacity) and foreground (high opacity) colors.
1825
+ */
1826
+ function generateCommunityColorsRGBA(communityCount) {
1827
+ const colors = [];
1828
+ const goldenRatio = .618033988749895;
1829
+ let hue = .1;
1830
+ for (let i = 0; i < communityCount; i++) {
1831
+ hue = (hue + goldenRatio) % 1;
1832
+ const h = hue * 360;
1833
+ const s = .45;
1834
+ const l = .55;
1835
+ const { r, g, b } = hslToRgb(h, s, l);
1836
+ colors.push({
1837
+ background: `rgba(${r}, ${g}, ${b}, 0.15)`,
1838
+ foreground: `rgba(${r}, ${g}, ${b}, 0.9)`,
1839
+ border: `rgba(${r}, ${g}, ${b}, 0.5)`
1840
+ });
1841
+ }
1842
+ return colors;
1843
+ }
1844
+ function hslToRgb(h, s, l) {
1845
+ h = h / 360;
1846
+ let r, g, b;
1847
+ if (s === 0) r = g = b = l;
1848
+ else {
1849
+ const hue2rgb = (p$1, q$1, t) => {
1850
+ if (t < 0) t += 1;
1851
+ if (t > 1) t -= 1;
1852
+ if (t < 1 / 6) return p$1 + (q$1 - p$1) * 6 * t;
1853
+ if (t < 1 / 2) return q$1;
1854
+ if (t < 2 / 3) return p$1 + (q$1 - p$1) * (2 / 3 - t) * 6;
1855
+ return p$1;
1856
+ };
1857
+ const q = l < .5 ? l * (1 + s) : l + s - l * s;
1858
+ const p = 2 * l - q;
1859
+ r = hue2rgb(p, q, h + 1 / 3);
1860
+ g = hue2rgb(p, q, h);
1861
+ b = hue2rgb(p, q, h - 1 / 3);
1862
+ }
1863
+ return {
1864
+ r: Math.round(r * 255),
1865
+ g: Math.round(g * 255),
1866
+ b: Math.round(b * 255)
1867
+ };
1868
+ }
1869
+
1692
1870
  //#endregion
1693
1871
  //#region src/suggest/filter.ts
1694
1872
  /**
@@ -1889,7 +2067,7 @@ let SuggestionType = /* @__PURE__ */ function(SuggestionType$1) {
1889
2067
  return SuggestionType$1;
1890
2068
  }({});
1891
2069
  function gen(graph, nodesUsedInTemplate, nodesUsedInStyle = /* @__PURE__ */ new Set(), options) {
1892
- const { ellipsis = true } = options ?? {};
2070
+ const { ellipsis = true, communitySeed } = options ?? {};
1893
2071
  const usedNodes = new Set([...nodesUsedInTemplate, ...nodesUsedInStyle]);
1894
2072
  const suggestions = [];
1895
2073
  const splitedGraph = splitGraph(graph.edges);
@@ -1943,6 +2121,48 @@ function gen(graph, nodesUsedInTemplate, nodesUsedInStyle = /* @__PURE__ */ new
1943
2121
  nodeInfo: node
1944
2122
  });
1945
2123
  });
2124
+ const communityResult = detectCommunities(graph.edges, { seed: communitySeed });
2125
+ const { communities } = communityResult;
2126
+ const extractableCommunities = communities.filter((community) => {
2127
+ const nodes = Array.from(community.nodes);
2128
+ if (nodes.length < 3 || nodes.length > 15) return false;
2129
+ const hasUsedNode = nodes.some((node) => usedNodes.has(node.label));
2130
+ const hasUnusedNode = nodes.some((node) => !usedNodes.has(node.label));
2131
+ return hasUsedNode && hasUnusedNode;
2132
+ });
2133
+ extractableCommunities.forEach((community) => {
2134
+ const nodes = Array.from(community.nodes);
2135
+ const unusedNodes = nodes.filter((node) => !usedNodes.has(node.label));
2136
+ if (unusedNodes.length >= 2) suggestions.push({
2137
+ type: SuggestionType.info,
2138
+ message: `Nodes [${ellipsis && nodes.length > 10 ? `${nodes.slice(0, 10).map((node) => node.label).join(",")}...(${nodes.length})` : nodes.map((node) => node.label).join(",")}] form a tightly coupled group, consider extracting them into a composable/hook.`,
2139
+ nodeInfo: nodes
2140
+ });
2141
+ });
2142
+ if (communities.length > 3) {
2143
+ const independentCommunities = communities.filter((community) => {
2144
+ const nodes = Array.from(community.nodes);
2145
+ return nodes.length >= 2 && nodes.every((node) => !usedNodes.has(node.label) && !node.info?.used?.size);
2146
+ });
2147
+ if (independentCommunities.length >= 2) {
2148
+ const allNodes = independentCommunities.flatMap((c) => Array.from(c.nodes));
2149
+ suggestions.push({
2150
+ type: SuggestionType.info,
2151
+ message: `Found ${independentCommunities.length} independent variable groups that are not used in template. Consider removing or extracting them.`,
2152
+ nodeInfo: allNodes
2153
+ });
2154
+ }
2155
+ }
2156
+ const largeCommunities = communities.filter((c) => c.nodes.size > 10);
2157
+ largeCommunities.forEach((community) => {
2158
+ const nodes = Array.from(community.nodes);
2159
+ const functionNodes = nodes.filter((n) => n.type === NodeType.fun);
2160
+ if (functionNodes.length > 5) suggestions.push({
2161
+ type: SuggestionType.warning,
2162
+ message: `Community with ${nodes.length} nodes has ${functionNodes.length} functions. This group is complex, consider splitting into smaller composables.`,
2163
+ nodeInfo: nodes
2164
+ });
2165
+ });
1946
2166
  return suggestions;
1947
2167
  }
1948
2168
 
@@ -2005,7 +2225,10 @@ exports.analyzeSetupScript = analyze$1;
2005
2225
  exports.analyzeStyle = analyze$2;
2006
2226
  exports.analyzeTemplate = analyze$3;
2007
2227
  exports.analyzeTsx = analyze$4;
2228
+ exports.detectCommunities = detectCommunities;
2008
2229
  exports.gen = gen;
2230
+ exports.generateCommunityColors = generateCommunityColors;
2231
+ exports.generateCommunityColorsRGBA = generateCommunityColorsRGBA;
2009
2232
  exports.getMermaidText = getMermaidText;
2010
2233
  exports.getVisData = getVisData;
2011
2234
  Object.defineProperty(exports, 'parse', {