Skip to content

Commit 02f4512

Browse files
committed
feat: separate bar component
1 parent e9e99c1 commit 02f4512

3 files changed

Lines changed: 317 additions & 241 deletions

File tree

app/components/Package/Dependencies.vue

Lines changed: 24 additions & 241 deletions
Original file line numberDiff line numberDiff line change
@@ -70,201 +70,17 @@ const sortedOptionalDependencies = computed(() => {
7070
})
7171
7272
// Fetch size information for dependencies that require it
73-
const { data: sizereqData, pending: sizereqLoading } = await useAsyncData(
74-
`sizes:${props.packageName}:${props.version}`,
75-
async (_app, { signal }) => {
76-
const entries = sortedDependencies.value
77-
78-
const results = await Promise.all(
79-
entries.map<
80-
Promise<
81-
{ kind: 'success'; packageSize: InstallSizeResult } | { kind: 'error'; error: NuxtError }
82-
>
83-
>(async ([name, version]) => {
84-
try {
85-
const { data: resolvedVersion, error } = await useResolvedVersion(name, version)
86-
87-
if (error.value || !resolvedVersion.value) return { kind: 'error', error: error.value! }
88-
89-
return {
90-
kind: 'success',
91-
packageSize: await $fetch<InstallSizeResult>(
92-
`/api/registry/install-size/${name}/v/${encodeURIComponent(resolvedVersion.value)}`,
93-
{ signal },
94-
),
95-
}
96-
} catch (err) {
97-
return { kind: 'error', error: (err as Ref<NuxtError>)?.value }
98-
}
99-
}),
100-
)
101-
102-
return results.reduce(
103-
(acc, curr) => {
104-
if (curr.kind === 'error') return acc
105-
acc[curr.packageSize.package] = curr
106-
return acc
107-
},
108-
{} as Record<
109-
string,
110-
{ kind: 'success'; packageSize: InstallSizeResult } | { kind: 'error'; error: NuxtError }
111-
>,
112-
)
113-
},
114-
{
115-
watch: [sortedDependencies],
116-
server: false,
117-
},
73+
const { data: sizereqData, pending: sizereqLoading } = usePackageDependencySizes(
74+
() => props.packageName,
75+
() => props.version,
76+
() => props.dependencies,
11877
)
11978
120-
// Minimum percentage to be shown as an individual slice
121-
const THRESHOLD_PERCENT = 2
122-
123-
type Sizereq = {
124-
info: InstallSizeResult
125-
bundled: boolean
126-
percent: number
127-
error: NuxtError | null
128-
}
129-
130-
// Process dependencies for size visualization
131-
const sortedSizereqDependecies = computed(() => {
132-
if (!props.packageSize?.totalSize || !props.packageSize.dependencies) {
133-
return { visible: [], others: [], totalOthersSize: 0, othersPercentage: 0 }
134-
}
135-
136-
const allMapped = props.packageSize.dependencies.map(depSize => {
137-
let bundled: boolean = false
138-
switch (typeof props.bundledDependencies) {
139-
case 'boolean':
140-
bundled = props.bundledDependencies
141-
break
142-
case 'object':
143-
bundled = props.bundledDependencies.some(name => name === depSize.name)
144-
break
145-
}
146-
const percent = props.packageSize ? (depSize.size / props.packageSize.totalSize) * 100 : 0
147-
const serverData = sizereqData.value?.[depSize.name]
148-
const error = serverData?.kind === 'error' ? serverData.error : null
149-
return {
150-
info:
151-
serverData?.kind === 'success' && serverData.packageSize
152-
? {
153-
package: depSize.name,
154-
version: depSize.version,
155-
totalSize: serverData.packageSize.totalSize,
156-
selfSize: serverData.packageSize.selfSize,
157-
}
158-
: {
159-
package: depSize.name,
160-
version: depSize.version,
161-
totalSize: depSize.size,
162-
selfSize: depSize.size,
163-
},
164-
error,
165-
bundled,
166-
percent,
167-
} as Sizereq
168-
})
169-
170-
const visible: Sizereq[] = []
171-
const others: Sizereq[] = []
172-
173-
for (const dep of allMapped) {
174-
const percentage = (dep.info.selfSize / props.packageSize.totalSize) * 100
175-
if (percentage >= THRESHOLD_PERCENT) {
176-
visible.push({ ...dep, percent: percentage })
177-
} else {
178-
others.push(dep)
179-
}
180-
}
181-
182-
const othersSelfSize = others.reduce((acc, d) => acc + d.info.selfSize, 0)
183-
const othersPercentage = (othersSelfSize / props.packageSize.totalSize) * 100
184-
185-
// if (others.length === 1) {
186-
// visible.push(others[0]!)
187-
// others.length = 0
188-
// visible.sort((a, b) => b.info.totalSize - a.info.totalSize)
189-
// } else if (others.length > 1 && othersPercentage < THRESHOLD_PERCENT) {
190-
// visible.push(...others)
191-
// others.length = 0
192-
// visible.sort((a, b) => b.info.totalSize - a.info.totalSize)
193-
// }
194-
195-
return { visible, others, totalOthersSize: othersSelfSize, othersPercentage }
196-
})
197-
198-
const othersTooltip = computed(() => {
199-
const others = sortedSizereqDependecies.value.others
200-
if (others.length === 0) return ''
201-
202-
const MAX_VISIBLE_IN_TOOLTIP = 0
203-
const visiblePart = others.slice(0, MAX_VISIBLE_IN_TOOLTIP)
204-
const remainingCount = others.length - MAX_VISIBLE_IN_TOOLTIP
205-
206-
const lines = [
207-
bytesFormatter.format(sortedSizereqDependecies.value.totalOthersSize),
208-
numberFormatter.value.format(sortedSizereqDependecies.value.othersPercentage),
209-
'',
210-
...visiblePart.flatMap(size => [size.info.package, getDepSizeTooltipText(size), '']),
211-
]
212-
213-
if (remainingCount > 0) {
214-
lines.push(t('package.size_increase.deps', { count: remainingCount }))
215-
}
216-
217-
return lines.join('\n')
218-
})
219-
220-
const selfSizeWidth = computed(() => {
221-
if (!props.packageSize?.selfSize || !props.packageSize?.totalSize) return 0
222-
return (props.packageSize.selfSize / props.packageSize.totalSize) * 100
223-
})
224-
225-
const remainingWidth = computed(() => {
226-
const total = props.packageSize?.totalSize
227-
if (!total) return 100
228-
229-
const self = props.packageSize.selfSize || 0
230-
const depsSum = [
231-
...sortedSizereqDependecies.value.visible,
232-
...sortedSizereqDependecies.value.others,
233-
].reduce((acc, d) => acc + d.info.selfSize, 0)
234-
235-
const width = ((total - (self + depsSum)) / total) * 100
236-
return Math.max(0, width)
237-
})
238-
239-
// Get dependency size tooltip
240-
function getDepSizeTooltip(dep: string): string | undefined {
241-
const size = [
242-
...sortedSizereqDependecies.value.visible,
243-
...sortedSizereqDependecies.value.others,
244-
].find(d => d.info.package === dep)
245-
return size && getDepSizeTooltipText(size)
246-
}
247-
248-
function getDepSizeTooltipText(size: Sizereq): string {
249-
const packageSize = size?.error ? undefined : size?.info
250-
const percent = size?.percent
251-
return [
252-
size?.error?.message,
253-
percent && numberFormatter.value.format(percent),
254-
packageSize &&
255-
packageSize?.totalSize !== packageSize?.selfSize &&
256-
t('package.stats.size_tooltip.unpacked', {
257-
size: bytesFormatter.format(packageSize.selfSize!),
258-
}),
259-
packageSize?.totalSize &&
260-
t('package.stats.size_tooltip.total', {
261-
count: packageSize.dependencyCount,
262-
size: bytesFormatter.format(packageSize.totalSize),
263-
}),
264-
]
265-
.filter(Boolean)
266-
.join('\n')
267-
}
79+
const { getTooltipText: getDepSizeTooltip } = usePackageDependencySizeTooltip(
80+
sizereqData,
81+
() => props.packageSize,
82+
t,
83+
)
26884
26985
// Get version tooltip
27086
function getDepVersionTooltip(dep: string, version: string) {
@@ -313,7 +129,7 @@ const bytesFormatter = useBytesFormatter()
313129
v-if="sortedDependencies.length > 0"
314130
id="dependencies"
315131
:title="
316-
$t(
132+
t(
317133
'package.dependencies.title',
318134
{
319135
count: numberFormatter.format(sortedDependencies.length),
@@ -322,46 +138,13 @@ const bytesFormatter = useBytesFormatter()
322138
)
323139
"
324140
>
325-
<div class="gap-0.5 flex flex-row h-6 w-full bg-fg-muted/10 overflow-hidden rounded-md">
326-
<TooltipApp
327-
v-if="selfSizeWidth > 0"
328-
:text="
329-
t('package.stats.size_tooltip.unpacked', {
330-
size: bytesFormatter.format(props.packageSize?.selfSize || 0),
331-
})
332-
"
333-
class="h-full bg-accent"
334-
:style="{ width: selfSizeWidth + '%' }"
335-
/>
336-
337-
<template v-for="dep in sortedSizereqDependecies.visible" :key="dep.info.package">
338-
<TooltipApp
339-
:text="`${dep.info.package}\n${getDepSizeTooltip(dep.info.package)}`"
340-
class="h-full"
341-
:class="dep.bundled ? 'bg-accent' : 'bg-fg'"
342-
:style="{ width: dep.percent + '%' }"
343-
>
344-
<RouterLink
345-
:to="packageRoute(dep.info.package, dep.info.version)"
346-
class="block w-full h-full"
347-
/>
348-
</TooltipApp>
349-
</template>
350-
351-
<TooltipApp
352-
v-if="sortedSizereqDependecies.others.length > 0"
353-
:text="othersTooltip"
354-
class="h-full bg-fg flex items-center justify-center"
355-
:style="{ width: sortedSizereqDependecies.othersPercentage + '%' }"
356-
>
357-
<span class="i-lucide:layers-2 w-3 h-3 text-bg" aria-hidden="true" />
358-
</TooltipApp>
359-
360-
<div
361-
v-if="remainingWidth > 0"
362-
class="h-full bg-bg-elevated animate-skeleton-pulse flex-1"
363-
/>
364-
</div>
141+
<PackageSizeBar
142+
:package-name="props.packageName"
143+
:version="props.version"
144+
:package-size="props.packageSize"
145+
:dependencies="props.dependencies"
146+
:bundled-dependencies="props.bundledDependencies"
147+
/>
365148
<ul class="space-y-1 list-none m-0" :aria-label="$t('package.dependencies.list_label')">
366149
<li
367150
v-for="[dep, version] in visibleDeps"
@@ -376,12 +159,12 @@ const bytesFormatter = useBytesFormatter()
376159
v-if="outdatedDeps[dep]"
377160
class="shrink-0"
378161
:class="getVersionClass(outdatedDeps[dep])"
379-
:text="getOutdatedTooltip(outdatedDeps[dep], $t)"
162+
:text="getOutdatedTooltip(outdatedDeps[dep], t)"
380163
>
381164
<button
382165
type="button"
383166
class="inline-flex items-center justify-center p-2 -m-2"
384-
:aria-label="getOutdatedTooltip(outdatedDeps[dep], $t)"
167+
:aria-label="getOutdatedTooltip(outdatedDeps[dep], t)"
385168
>
386169
<span class="i-lucide:circle-alert w-3 h-3" aria-hidden="true" />
387170
</button>
@@ -451,7 +234,7 @@ const bytesFormatter = useBytesFormatter()
451234
</button>
452235
</TooltipApp>
453236
<span v-if="outdatedDeps[dep]" class="sr-only">
454-
({{ getOutdatedTooltip(outdatedDeps[dep], $t) }})
237+
({{ getOutdatedTooltip(outdatedDeps[dep], t) }})
455238
</span>
456239
<span v-if="getVulnerableDepInfo(dep)" class="sr-only">
457240
({{
@@ -470,7 +253,7 @@ const bytesFormatter = useBytesFormatter()
470253
@click="expandDeps"
471254
>
472255
{{
473-
$t(
256+
t(
474257
'package.dependencies.show_all',
475258
{
476259
count: numberFormatter.format(sortedDependencies.length),
@@ -525,7 +308,7 @@ const bytesFormatter = useBytesFormatter()
525308
@click="expandPeerDeps"
526309
>
527310
{{
528-
$t(
311+
t(
529312
'package.peer_dependencies.show_all',
530313
{
531314
count: numberFormatter.format(sortedPeerDependencies.length),
@@ -541,7 +324,7 @@ const bytesFormatter = useBytesFormatter()
541324
v-if="sortedOptionalDependencies.length > 0"
542325
id="optional-dependencies"
543326
:title="
544-
$t(
327+
t(
545328
'package.optional_dependencies.title',
546329
{
547330
count: numberFormatter.format(sortedOptionalDependencies.length),
@@ -579,7 +362,7 @@ const bytesFormatter = useBytesFormatter()
579362
@click="expandOptionalDeps"
580363
>
581364
{{
582-
$t(
365+
t(
583366
'package.optional_dependencies.show_all',
584367
{
585368
count: numberFormatter.format(sortedOptionalDependencies.length),

0 commit comments

Comments
 (0)