Optimized package analysis

This commit is contained in:
2026-04-14 23:23:23 +05:30
parent be8d796bb3
commit 746593c60d

View File

@@ -1,7 +1,7 @@
import { Button } from "./components/ui/button";
import { Label } from "./components/ui/label";
import { Input } from "./components/ui/input";
import { useState, useEffect, useRef } from "react";
import { useState, useEffect, useRef, useMemo, useCallback, memo } from "react";
import { Edit2, ChevronDown, ChevronUp, AlertCircle, Search, Package, Upload, CheckCircle2, XCircle, AlertTriangle, FileJson, TrendingUp, TrendingDown, Copy, Check, Terminal, X, Save, FolderOpen } from "lucide-react";
import { toast } from "sonner";
import { PackageSelector } from "./PackageSelector";
@@ -98,7 +98,7 @@ function satisfiesVersion(installedVersion: string, requiredRange: string): bool
}
}
function PackageResultCard({ result, onVersionChange, nodeVersion }: {
const PackageResultCard = memo(function PackageResultCard({ result, onVersionChange, nodeVersion }: {
result: AnalysisResult;
onVersionChange: (packageName: string, version: string) => void;
nodeVersion: string;
@@ -111,10 +111,23 @@ function PackageResultCard({ result, onVersionChange, nodeVersion }: {
const hasVersionChange = result.selectedVersion && result.selectedVersion !== result.currentVersion;
const hasCompatibilityIssue = !result.nodeCompatible || (result.peerConflicts && result.peerConflicts.length > 0);
const sortedVersions = result.availableVersions ? sortVersions([...result.availableVersions]) : [];
const filteredAvailableVersions = sortedVersions.filter(v =>
v.toLowerCase().includes(versionSearchQuery.toLowerCase())
const sortedVersions = useMemo(() =>
result.availableVersions ? sortVersions([...result.availableVersions]) : [],
[result.availableVersions]
);
const filteredAvailableVersions = useMemo(() =>
sortedVersions.filter(v =>
v.toLowerCase().includes(versionSearchQuery.toLowerCase())
),
[sortedVersions, versionSearchQuery]
);
const handleVersionChange = useCallback((version: string) => {
onVersionChange(result.name, version);
setVersionDropdownOpen(false);
setVersionSearchQuery("");
}, [result.name, onVersionChange]);
useEffect(() => {
if (!versionDropdownOpen) return;
@@ -233,11 +246,7 @@ function PackageResultCard({ result, onVersionChange, nodeVersion }: {
filteredAvailableVersions.map((version) => (
<button
key={version}
onClick={() => {
onVersionChange(result.name, version);
setVersionDropdownOpen(false);
setVersionSearchQuery('');
}}
onClick={() => handleVersionChange(version)}
className={`w-full text-left px-3 py-2 rounded-md transition-colors ${
version === activeVersion
? 'bg-accent font-medium border border-border'
@@ -419,7 +428,7 @@ function PackageResultCard({ result, onVersionChange, nodeVersion }: {
</div>
</div>
);
}
});
export function PackageDetails() {
const [packages, setPackages] = useState<PackageData[]>([]);
@@ -480,7 +489,7 @@ export function PackageDetails() {
});
};
const handleVersionChange = (packageName: string, version: string) => {
const handleVersionChange = useCallback((packageName: string, version: string) => {
const newSelections = new Map(versionSelections);
newSelections.set(packageName, version);
setVersionSelections(newSelections);
@@ -505,7 +514,7 @@ export function PackageDetails() {
} else {
toast.info(`${packageName}${version}`);
}
};
}, [versionSelections, analysisResults, nodeVersion]);
useEffect(() => {
if (analysisResults.length > 0) {
@@ -995,44 +1004,69 @@ export function PackageDetails() {
}
}
const selectedPkgs = packages.filter(p => selectedPackages.has(p.name));
const filteredVersions = nodeVersions.filter(v => {
const searchLower = nodeVersionSearch.toLowerCase();
const matchesVersion = v.version.toLowerCase().includes(searchLower);
const matchesLtsName = typeof v.lts === 'string' && v.lts.toLowerCase().includes(searchLower);
const matchesLtsKeyword = v.lts && searchLower.includes('lts');
return matchesVersion || matchesLtsName || matchesLtsKeyword;
});
const selectedPkgs = useMemo(() =>
packages.filter(p => selectedPackages.has(p.name)),
[packages, selectedPackages]
);
const filteredVersions = useMemo(() =>
nodeVersions.filter(v => {
const searchLower = nodeVersionSearch.toLowerCase();
const matchesVersion = v.version.toLowerCase().includes(searchLower);
const matchesLtsName = typeof v.lts === 'string' && v.lts.toLowerCase().includes(searchLower);
const matchesLtsKeyword = v.lts && searchLower.includes('lts');
return matchesVersion || matchesLtsName || matchesLtsKeyword;
}),
[nodeVersions, nodeVersionSearch]
);
const selectedVersionInfo = nodeVersions.find(v => v.version.replace('v', '') === nodeVersion);
const selectedVersionInfo = useMemo(() =>
nodeVersions.find(v => v.version.replace('v', '') === nodeVersion),
[nodeVersions, nodeVersion]
);
// Count issues
const compatibilityIssues = analysisResults.filter(r => !r.nodeCompatible || (r.peerConflicts && r.peerConflicts.length > 0)).length;
const modifiedPackages = analysisResults.filter(r => r.selectedVersion && r.selectedVersion !== r.currentVersion).length;
const compatibilityIssues = useMemo(() =>
analysisResults.filter(r => !r.nodeCompatible || (r.peerConflicts && r.peerConflicts.length > 0)).length,
[analysisResults]
);
const modifiedPackages = useMemo(() =>
analysisResults.filter(r => r.selectedVersion && r.selectedVersion !== r.currentVersion).length,
[analysisResults]
);
// Get packages with compatibility issues among modified packages
const modifiedWithIssues = analysisResults.filter(r =>
r.selectedVersion &&
r.selectedVersion !== r.currentVersion &&
(!r.nodeCompatible || (r.peerConflicts && r.peerConflicts.length > 0))
const modifiedWithIssues = useMemo(() =>
analysisResults.filter(r =>
r.selectedVersion &&
r.selectedVersion !== r.currentVersion &&
(!r.nodeCompatible || (r.peerConflicts && r.peerConflicts.length > 0))
),
[analysisResults]
);
// Get unmodified packages with issues
const unmodifiedWithIssues = analysisResults.filter(r =>
(!r.selectedVersion || r.selectedVersion === r.currentVersion) &&
(!r.nodeCompatible || (r.peerConflicts && r.peerConflicts.length > 0))
const unmodifiedWithIssues = useMemo(() =>
analysisResults.filter(r =>
(!r.selectedVersion || r.selectedVersion === r.currentVersion) &&
(!r.nodeCompatible || (r.peerConflicts && r.peerConflicts.length > 0))
),
[analysisResults]
);
// Generate npm install command
const generateUpgradeCommand = () => {
const upgradeCommand = useMemo(() => {
const modifiedPkgs = analysisResults.filter(r => r.selectedVersion && r.selectedVersion !== r.currentVersion);
if (modifiedPkgs.length === 0) return '';
const packages = modifiedPkgs.map(pkg => `${pkg.name}@${pkg.selectedVersion}`).join(' ');
return `npm install ${packages}`;
};
}, [analysisResults]);
const copyToClipboard = async (text: string) => {
const generateUpgradeCommand = () => upgradeCommand;
const copyToClipboard = useCallback(async (text: string) => {
try {
await navigator.clipboard.writeText(text);
setCopiedCommand(true);
@@ -1041,7 +1075,7 @@ export function PackageDetails() {
} catch (err) {
toast.error('Failed to copy to clipboard');
}
};
}, []);
return (
<div className="bg-background">