Skip to content
Merged
106 changes: 56 additions & 50 deletions app/composables/useFacetSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,56 +23,62 @@ export interface FacetInfoWithLabels extends Omit<FacetInfo, 'id'> {
export function useFacetSelection(queryParam = 'facets') {
const { t } = useI18n()

const facetLabels = computed(() => ({
downloads: {
label: t(`compare.facets.items.downloads.label`),
description: t(`compare.facets.items.downloads.description`),
},
packageSize: {
label: t(`compare.facets.items.packageSize.label`),
description: t(`compare.facets.items.packageSize.description`),
},
installSize: {
label: t(`compare.facets.items.installSize.label`),
description: t(`compare.facets.items.installSize.description`),
},
moduleFormat: {
label: t(`compare.facets.items.moduleFormat.label`),
description: t(`compare.facets.items.moduleFormat.description`),
},
types: {
label: t(`compare.facets.items.types.label`),
description: t(`compare.facets.items.types.description`),
},
engines: {
label: t(`compare.facets.items.engines.label`),
description: t(`compare.facets.items.engines.description`),
},
vulnerabilities: {
label: t(`compare.facets.items.vulnerabilities.label`),
description: t(`compare.facets.items.vulnerabilities.description`),
},
lastUpdated: {
label: t(`compare.facets.items.lastUpdated.label`),
description: t(`compare.facets.items.lastUpdated.description`),
},
license: {
label: t(`compare.facets.items.license.label`),
description: t(`compare.facets.items.license.description`),
},
dependencies: {
label: t(`compare.facets.items.dependencies.label`),
description: t(`compare.facets.items.dependencies.description`),
},
totalDependencies: {
label: t(`compare.facets.items.totalDependencies.label`),
description: t(`compare.facets.items.totalDependencies.description`),
},
deprecated: {
label: t(`compare.facets.items.deprecated.label`),
description: t(`compare.facets.items.deprecated.description`),
},
}))
const facetLabels = computed(
(): Record<ComparisonFacet, { label: string; description: string }> => ({
downloads: {
label: t(`compare.facets.items.downloads.label`),
description: t(`compare.facets.items.downloads.description`),
},
totalLikes: {
label: t(`compare.facets.items.totalLikes.label`),
description: t(`compare.facets.items.totalLikes.description`),
},
packageSize: {
label: t(`compare.facets.items.packageSize.label`),
description: t(`compare.facets.items.packageSize.description`),
},
installSize: {
label: t(`compare.facets.items.installSize.label`),
description: t(`compare.facets.items.installSize.description`),
},
moduleFormat: {
label: t(`compare.facets.items.moduleFormat.label`),
description: t(`compare.facets.items.moduleFormat.description`),
},
types: {
label: t(`compare.facets.items.types.label`),
description: t(`compare.facets.items.types.description`),
},
engines: {
label: t(`compare.facets.items.engines.label`),
description: t(`compare.facets.items.engines.description`),
},
vulnerabilities: {
label: t(`compare.facets.items.vulnerabilities.label`),
description: t(`compare.facets.items.vulnerabilities.description`),
},
lastUpdated: {
label: t(`compare.facets.items.lastUpdated.label`),
description: t(`compare.facets.items.lastUpdated.description`),
},
license: {
label: t(`compare.facets.items.license.label`),
description: t(`compare.facets.items.license.description`),
},
dependencies: {
label: t(`compare.facets.items.dependencies.label`),
description: t(`compare.facets.items.dependencies.description`),
},
totalDependencies: {
label: t(`compare.facets.items.totalDependencies.label`),
description: t(`compare.facets.items.totalDependencies.description`),
},
deprecated: {
label: t(`compare.facets.items.deprecated.label`),
description: t(`compare.facets.items.deprecated.description`),
},
}),
)

// Helper to build facet info with i18n labels
function buildFacetInfo(facet: ComparisonFacet): FacetInfoWithLabels {
Expand Down
17 changes: 15 additions & 2 deletions app/composables/usePackageComparison.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
Packument,
VulnerabilityTreeResult,
} from '#shared/types'
import type { PackageLikes } from '#shared/types/social'
import { encodePackageName } from '#shared/utils/npm'
import type { PackageAnalysisResponse } from './usePackageAnalysis'
import { isBinaryOnlyPackage } from '#shared/utils/binary-detection'
Expand All @@ -28,6 +29,8 @@ export const NoDependencyDisplay = {
export interface PackageComparisonData {
package: ComparisonPackage
downloads?: number
/** Total likes from atproto */
totalLikes?: number
/** Package's own unpacked size (from dist.unpackedSize) */
packageSize?: number
/** Number of direct dependencies */
Expand Down Expand Up @@ -127,7 +130,7 @@ export function usePackageComparison(packageNames: MaybeRefOrGetter<string[]>) {
if (!latestVersion) return null

// Fetch fast additional data in parallel (optional - failures are ok)
const [downloads, analysis, vulns] = await Promise.all([
const [downloads, analysis, vulns, likes] = await Promise.all([
$fetch<{ downloads: number }>(
`https://api.npmjs.org/downloads/point/last-week/${encodePackageName(name)}`,
).catch(() => null),
Expand All @@ -137,8 +140,8 @@ export function usePackageComparison(packageNames: MaybeRefOrGetter<string[]>) {
$fetch<VulnerabilityTreeResult>(
`/api/registry/vulnerabilities/${encodePackageName(name)}`,
).catch(() => null),
$fetch<PackageLikes>(`/api/social/likes/${name}`).catch(() => null),
])

const versionData = pkgData.versions[latestVersion]
const packageSize = versionData?.dist?.unpackedSize

Expand Down Expand Up @@ -188,6 +191,7 @@ export function usePackageComparison(packageNames: MaybeRefOrGetter<string[]>) {
deprecated: versionData?.deprecated,
},
isBinaryOnly: isBinary,
totalLikes: likes?.totalLikes,
}
} catch {
return null
Expand Down Expand Up @@ -299,6 +303,7 @@ function createNoDependencyData(): PackageComparisonData {
},
isNoDependency: true,
downloads: undefined,
totalLikes: undefined,
packageSize: 0,
directDeps: 0,
installSize: {
Expand Down Expand Up @@ -353,6 +358,14 @@ function computeFacetValue(
status: 'neutral',
}
}
case 'totalLikes': {
if (data.totalLikes === undefined) return null
return {
raw: data.totalLikes,
display: formatCompactNumber(data.totalLikes),
status: 'neutral',
}
}
case 'packageSize': {
// A size of zero is valid
if (data.packageSize == null) return null
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,10 @@
"label": "Downloads/wk",
"description": "Weekly download count"
},
"totalLikes": {
"label": "Likes",
"description": "Number of likes"
},
"lastUpdated": {
"label": "Published",
"description": "When this version was published"
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,10 @@
"label": "Téléch./semaine",
"description": "Nombre de téléchargements par semaine"
},
"totalLikes": {
"label": "Likes",
"description": "Nombre de likes"
},
"lastUpdated": {
"label": "Publié",
"description": "Quand cette version a été publiée"
Expand Down
4 changes: 4 additions & 0 deletions lunaria/files/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,10 @@
"label": "Downloads/wk",
"description": "Weekly download count"
},
"totalLikes": {
"label": "Likes",
"description": "Number of likes"
},
"lastUpdated": {
"label": "Published",
"description": "When this version was published"
Expand Down
4 changes: 4 additions & 0 deletions lunaria/files/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,10 @@
"label": "Downloads/wk",
"description": "Weekly download count"
},
"totalLikes": {
"label": "Likes",
"description": "Number of likes"
},
"lastUpdated": {
"label": "Published",
"description": "When this version was published"
Expand Down
4 changes: 4 additions & 0 deletions lunaria/files/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,10 @@
"label": "Téléch./semaine",
"description": "Nombre de téléchargements par semaine"
},
"totalLikes": {
"label": "Likes",
"description": "Nombre de likes"
},
"lastUpdated": {
"label": "Publié",
"description": "Quand cette version a été publiée"
Expand Down
4 changes: 4 additions & 0 deletions shared/types/comparison.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type ComparisonFacet =
| 'dependencies'
| 'totalDependencies'
| 'deprecated'
| 'totalLikes'

/** Facet metadata for UI display */
export interface FacetInfo {
Expand Down Expand Up @@ -46,6 +47,9 @@ export const FACET_INFO: Record<ComparisonFacet, Omit<FacetInfo, 'id'>> = {
downloads: {
category: 'health',
},
totalLikes: {
category: 'health',
},
lastUpdated: {
category: 'health',
},
Expand Down
1 change: 1 addition & 0 deletions test/nuxt/components/compare/FacetSelector.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const facetLabels: Record<ComparisonFacet, { label: string; description: string
description: 'Total number of dependencies including transitive',
},
deprecated: { label: 'Deprecated?', description: 'Whether the package is deprecated' },
totalLikes: { label: 'Likes', description: 'Number of likes' },
}

const categoryLabels: Record<string, string> = {
Expand Down
Loading