vue-hook-optimizer 0.0.79 → 0.0.81
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 +213 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -2
- package/dist/index.d.ts +42 -2
- package/dist/index.js +211 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1689,6 +1689,169 @@ 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 sortNodesDeterministically(nodes) {
|
|
1707
|
+
return [...nodes].sort((a, b) => a.label.localeCompare(b.label));
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* Label Propagation Algorithm for community detection.
|
|
1711
|
+
*
|
|
1712
|
+
* Each node starts with its own unique label. In each iteration,
|
|
1713
|
+
* nodes adopt the most frequent label among their neighbors.
|
|
1714
|
+
* The algorithm converges when labels no longer change.
|
|
1715
|
+
*
|
|
1716
|
+
* This helps identify groups of nodes that are tightly connected
|
|
1717
|
+
* and could potentially be extracted into separate hooks.
|
|
1718
|
+
*/
|
|
1719
|
+
function detectCommunities(graph, maxIterations = 100) {
|
|
1720
|
+
const undirectedGraph = buildUndirectedGraph(graph);
|
|
1721
|
+
const nodes = Array.from(undirectedGraph.keys());
|
|
1722
|
+
if (nodes.length === 0) return {
|
|
1723
|
+
communities: [],
|
|
1724
|
+
nodeToCommuntiy: /* @__PURE__ */ new Map()
|
|
1725
|
+
};
|
|
1726
|
+
const labels = /* @__PURE__ */ new Map();
|
|
1727
|
+
nodes.forEach((node, index) => {
|
|
1728
|
+
labels.set(node, index);
|
|
1729
|
+
});
|
|
1730
|
+
let changed = true;
|
|
1731
|
+
let iterations = 0;
|
|
1732
|
+
while (changed && iterations < maxIterations) {
|
|
1733
|
+
changed = false;
|
|
1734
|
+
iterations++;
|
|
1735
|
+
const sortedNodes = sortNodesDeterministically(nodes);
|
|
1736
|
+
for (const node of sortedNodes) {
|
|
1737
|
+
const neighbors = undirectedGraph.get(node);
|
|
1738
|
+
if (!neighbors || neighbors.size === 0) continue;
|
|
1739
|
+
const labelCounts = /* @__PURE__ */ new Map();
|
|
1740
|
+
for (const neighbor of neighbors) {
|
|
1741
|
+
const neighborLabel = labels.get(neighbor);
|
|
1742
|
+
labelCounts.set(neighborLabel, (labelCounts.get(neighborLabel) || 0) + 1);
|
|
1743
|
+
}
|
|
1744
|
+
let maxCount = 0;
|
|
1745
|
+
let maxLabels = [];
|
|
1746
|
+
for (const [label, count] of labelCounts) if (count > maxCount) {
|
|
1747
|
+
maxCount = count;
|
|
1748
|
+
maxLabels = [label];
|
|
1749
|
+
} else if (count === maxCount) maxLabels.push(label);
|
|
1750
|
+
const currentLabel = labels.get(node);
|
|
1751
|
+
if (maxLabels.includes(currentLabel)) continue;
|
|
1752
|
+
const newLabel = Math.min(...maxLabels);
|
|
1753
|
+
if (newLabel !== currentLabel) {
|
|
1754
|
+
labels.set(node, newLabel);
|
|
1755
|
+
changed = true;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
const communityMap = /* @__PURE__ */ new Map();
|
|
1760
|
+
for (const [node, label] of labels) {
|
|
1761
|
+
if (!communityMap.has(label)) communityMap.set(label, /* @__PURE__ */ new Set());
|
|
1762
|
+
communityMap.get(label).add(node);
|
|
1763
|
+
}
|
|
1764
|
+
const communities = [];
|
|
1765
|
+
const nodeToCommuntiy = /* @__PURE__ */ new Map();
|
|
1766
|
+
let communityId = 0;
|
|
1767
|
+
for (const [_, nodeSet] of communityMap) {
|
|
1768
|
+
communities.push({
|
|
1769
|
+
id: communityId,
|
|
1770
|
+
nodes: nodeSet
|
|
1771
|
+
});
|
|
1772
|
+
for (const node of nodeSet) nodeToCommuntiy.set(node, communityId);
|
|
1773
|
+
communityId++;
|
|
1774
|
+
}
|
|
1775
|
+
communities.sort((a, b) => b.nodes.size - a.nodes.size);
|
|
1776
|
+
const reindexedCommunities = [];
|
|
1777
|
+
const newNodeToCommunity = /* @__PURE__ */ new Map();
|
|
1778
|
+
communities.forEach((community, newId) => {
|
|
1779
|
+
reindexedCommunities.push({
|
|
1780
|
+
id: newId,
|
|
1781
|
+
nodes: community.nodes
|
|
1782
|
+
});
|
|
1783
|
+
for (const node of community.nodes) newNodeToCommunity.set(node, newId);
|
|
1784
|
+
});
|
|
1785
|
+
return {
|
|
1786
|
+
communities: reindexedCommunities,
|
|
1787
|
+
nodeToCommuntiy: newNodeToCommunity
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
/**
|
|
1791
|
+
* Generate HSL colors for communities.
|
|
1792
|
+
* Uses golden ratio to distribute hues evenly.
|
|
1793
|
+
*/
|
|
1794
|
+
function generateCommunityColors(communityCount) {
|
|
1795
|
+
const colors = [];
|
|
1796
|
+
const goldenRatio = .618033988749895;
|
|
1797
|
+
let hue = .1;
|
|
1798
|
+
for (let i = 0; i < communityCount; i++) {
|
|
1799
|
+
hue = (hue + goldenRatio) % 1;
|
|
1800
|
+
const h = Math.floor(hue * 360);
|
|
1801
|
+
const s = 65 + i % 3 * 10;
|
|
1802
|
+
const l = 45 + i % 2 * 10;
|
|
1803
|
+
colors.push(`hsl(${h}, ${s}%, ${l}%)`);
|
|
1804
|
+
}
|
|
1805
|
+
return colors;
|
|
1806
|
+
}
|
|
1807
|
+
/**
|
|
1808
|
+
* Generate RGBA colors for VS Code decorations.
|
|
1809
|
+
* Returns both background (low opacity) and foreground (high opacity) colors.
|
|
1810
|
+
*/
|
|
1811
|
+
function generateCommunityColorsRGBA(communityCount) {
|
|
1812
|
+
const colors = [];
|
|
1813
|
+
const goldenRatio = .618033988749895;
|
|
1814
|
+
let hue = .1;
|
|
1815
|
+
for (let i = 0; i < communityCount; i++) {
|
|
1816
|
+
hue = (hue + goldenRatio) % 1;
|
|
1817
|
+
const h = hue * 360;
|
|
1818
|
+
const s = .45;
|
|
1819
|
+
const l = .55;
|
|
1820
|
+
const { r, g, b } = hslToRgb(h, s, l);
|
|
1821
|
+
colors.push({
|
|
1822
|
+
background: `rgba(${r}, ${g}, ${b}, 0.15)`,
|
|
1823
|
+
foreground: `rgba(${r}, ${g}, ${b}, 0.9)`,
|
|
1824
|
+
border: `rgba(${r}, ${g}, ${b}, 0.5)`
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
return colors;
|
|
1828
|
+
}
|
|
1829
|
+
function hslToRgb(h, s, l) {
|
|
1830
|
+
h = h / 360;
|
|
1831
|
+
let r, g, b;
|
|
1832
|
+
if (s === 0) r = g = b = l;
|
|
1833
|
+
else {
|
|
1834
|
+
const hue2rgb = (p$1, q$1, t) => {
|
|
1835
|
+
if (t < 0) t += 1;
|
|
1836
|
+
if (t > 1) t -= 1;
|
|
1837
|
+
if (t < 1 / 6) return p$1 + (q$1 - p$1) * 6 * t;
|
|
1838
|
+
if (t < 1 / 2) return q$1;
|
|
1839
|
+
if (t < 2 / 3) return p$1 + (q$1 - p$1) * (2 / 3 - t) * 6;
|
|
1840
|
+
return p$1;
|
|
1841
|
+
};
|
|
1842
|
+
const q = l < .5 ? l * (1 + s) : l + s - l * s;
|
|
1843
|
+
const p = 2 * l - q;
|
|
1844
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
1845
|
+
g = hue2rgb(p, q, h);
|
|
1846
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
1847
|
+
}
|
|
1848
|
+
return {
|
|
1849
|
+
r: Math.round(r * 255),
|
|
1850
|
+
g: Math.round(g * 255),
|
|
1851
|
+
b: Math.round(b * 255)
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1692
1855
|
//#endregion
|
|
1693
1856
|
//#region src/suggest/filter.ts
|
|
1694
1857
|
/**
|
|
@@ -1888,7 +2051,8 @@ let SuggestionType = /* @__PURE__ */ function(SuggestionType$1) {
|
|
|
1888
2051
|
SuggestionType$1["error"] = "error";
|
|
1889
2052
|
return SuggestionType$1;
|
|
1890
2053
|
}({});
|
|
1891
|
-
function gen(graph, nodesUsedInTemplate, nodesUsedInStyle = /* @__PURE__ */ new Set()) {
|
|
2054
|
+
function gen(graph, nodesUsedInTemplate, nodesUsedInStyle = /* @__PURE__ */ new Set(), options) {
|
|
2055
|
+
const { ellipsis = true } = options ?? {};
|
|
1892
2056
|
const usedNodes = new Set([...nodesUsedInTemplate, ...nodesUsedInStyle]);
|
|
1893
2057
|
const suggestions = [];
|
|
1894
2058
|
const splitedGraph = splitGraph(graph.edges);
|
|
@@ -1897,13 +2061,13 @@ function gen(graph, nodesUsedInTemplate, nodesUsedInStyle = /* @__PURE__ */ new
|
|
|
1897
2061
|
if (splitedGraph.length > 1) {
|
|
1898
2062
|
if (nodes.length > 2 && nodes.some((node) => !usedNodes.has(node.label))) suggestions.push({
|
|
1899
2063
|
type: SuggestionType.info,
|
|
1900
|
-
message: `Nodes [${nodes.length > 10 ? `${nodes.slice(0, 10).map((node) => node.label).join(",")}...(${nodes.length})` : nodes.map((node) => node.label).join(",")}] are isolated, perhaps you can refactor them to an isolated file.`,
|
|
2064
|
+
message: `Nodes [${ellipsis && nodes.length > 10 ? `${nodes.slice(0, 10).map((node) => node.label).join(",")}...(${nodes.length})` : nodes.map((node) => node.label).join(",")}] are isolated, perhaps you can refactor them to an isolated file.`,
|
|
1901
2065
|
nodeInfo: nodes
|
|
1902
2066
|
});
|
|
1903
2067
|
}
|
|
1904
2068
|
if (nodes.length > 1 && nodes.every((node) => !usedNodes.has(node.label) && !node.info?.used?.size)) suggestions.push({
|
|
1905
2069
|
type: SuggestionType.info,
|
|
1906
|
-
message: `Nodes [${nodes.length > 10 ? `${nodes.slice(0, 10).map((node) => node.label).join(",")}...` : nodes.map((node) => node.label).join(",")}] are not used, perhaps you can remove them.`,
|
|
2070
|
+
message: `Nodes [${ellipsis && nodes.length > 10 ? `${nodes.slice(0, 10).map((node) => node.label).join(",")}...` : nodes.map((node) => node.label).join(",")}] are not used, perhaps you can remove them.`,
|
|
1907
2071
|
nodeInfo: nodes
|
|
1908
2072
|
});
|
|
1909
2073
|
const hasCycleResult = hasCycle(g);
|
|
@@ -1919,7 +2083,7 @@ function gen(graph, nodesUsedInTemplate, nodesUsedInStyle = /* @__PURE__ */ new
|
|
|
1919
2083
|
const lastNotUsedNodeIndex = reverseLastNotUsedNodeIndex !== -1 ? path.length - 1 - reverseLastNotUsedNodeIndex : -1;
|
|
1920
2084
|
if (firstUsedNodeIndex > -1 && firstUsedNodeIndex < lastNotUsedNodeIndex) suggestions.push({
|
|
1921
2085
|
type: SuggestionType.warning,
|
|
1922
|
-
message: `Nodes [${path.length > 10 ? `${path.slice(0, 10).map((node) => node.label).join(",")}...(${path.length})` : path.map((node) => node.label).join(",")}] are have function chain calls, perhaps you can refactor it.`,
|
|
2086
|
+
message: `Nodes [${ellipsis && path.length > 10 ? `${path.slice(0, 10).map((node) => node.label).join(",")}...(${path.length})` : path.map((node) => node.label).join(",")}] are have function chain calls, perhaps you can refactor it.`,
|
|
1923
2087
|
nodeInfo: path
|
|
1924
2088
|
});
|
|
1925
2089
|
});
|
|
@@ -1942,6 +2106,48 @@ function gen(graph, nodesUsedInTemplate, nodesUsedInStyle = /* @__PURE__ */ new
|
|
|
1942
2106
|
nodeInfo: node
|
|
1943
2107
|
});
|
|
1944
2108
|
});
|
|
2109
|
+
const communityResult = detectCommunities(graph.edges);
|
|
2110
|
+
const { communities } = communityResult;
|
|
2111
|
+
const extractableCommunities = communities.filter((community) => {
|
|
2112
|
+
const nodes = Array.from(community.nodes);
|
|
2113
|
+
if (nodes.length < 3 || nodes.length > 15) return false;
|
|
2114
|
+
const hasUsedNode = nodes.some((node) => usedNodes.has(node.label));
|
|
2115
|
+
const hasUnusedNode = nodes.some((node) => !usedNodes.has(node.label));
|
|
2116
|
+
return hasUsedNode && hasUnusedNode;
|
|
2117
|
+
});
|
|
2118
|
+
extractableCommunities.forEach((community) => {
|
|
2119
|
+
const nodes = Array.from(community.nodes);
|
|
2120
|
+
const unusedNodes = nodes.filter((node) => !usedNodes.has(node.label));
|
|
2121
|
+
if (unusedNodes.length >= 2) suggestions.push({
|
|
2122
|
+
type: SuggestionType.info,
|
|
2123
|
+
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.`,
|
|
2124
|
+
nodeInfo: nodes
|
|
2125
|
+
});
|
|
2126
|
+
});
|
|
2127
|
+
if (communities.length > 3) {
|
|
2128
|
+
const independentCommunities = communities.filter((community) => {
|
|
2129
|
+
const nodes = Array.from(community.nodes);
|
|
2130
|
+
return nodes.length >= 2 && nodes.every((node) => !usedNodes.has(node.label) && !node.info?.used?.size);
|
|
2131
|
+
});
|
|
2132
|
+
if (independentCommunities.length >= 2) {
|
|
2133
|
+
const allNodes = independentCommunities.flatMap((c) => Array.from(c.nodes));
|
|
2134
|
+
suggestions.push({
|
|
2135
|
+
type: SuggestionType.info,
|
|
2136
|
+
message: `Found ${independentCommunities.length} independent variable groups that are not used in template. Consider removing or extracting them.`,
|
|
2137
|
+
nodeInfo: allNodes
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
const largeCommunities = communities.filter((c) => c.nodes.size > 10);
|
|
2142
|
+
largeCommunities.forEach((community) => {
|
|
2143
|
+
const nodes = Array.from(community.nodes);
|
|
2144
|
+
const functionNodes = nodes.filter((n) => n.type === NodeType.fun);
|
|
2145
|
+
if (functionNodes.length > 5) suggestions.push({
|
|
2146
|
+
type: SuggestionType.warning,
|
|
2147
|
+
message: `Community with ${nodes.length} nodes has ${functionNodes.length} functions. This group is complex, consider splitting into smaller composables.`,
|
|
2148
|
+
nodeInfo: nodes
|
|
2149
|
+
});
|
|
2150
|
+
});
|
|
1945
2151
|
return suggestions;
|
|
1946
2152
|
}
|
|
1947
2153
|
|
|
@@ -2004,7 +2210,10 @@ exports.analyzeSetupScript = analyze$1;
|
|
|
2004
2210
|
exports.analyzeStyle = analyze$2;
|
|
2005
2211
|
exports.analyzeTemplate = analyze$3;
|
|
2006
2212
|
exports.analyzeTsx = analyze$4;
|
|
2213
|
+
exports.detectCommunities = detectCommunities;
|
|
2007
2214
|
exports.gen = gen;
|
|
2215
|
+
exports.generateCommunityColors = generateCommunityColors;
|
|
2216
|
+
exports.generateCommunityColorsRGBA = generateCommunityColorsRGBA;
|
|
2008
2217
|
exports.getMermaidText = getMermaidText;
|
|
2009
2218
|
exports.getVisData = getVisData;
|
|
2010
2219
|
Object.defineProperty(exports, 'parse', {
|