11<script setup lang="ts">
2+ import { useOutdatedDependencies , getOutdatedTooltip } from ' ~/composables/useNpmRegistry'
3+ import type { OutdatedDependencyInfo } from ' ~/composables/useNpmRegistry'
4+
25const props = defineProps <{
36 packageName: string
47 dependencies? : Record <string , string >
58 peerDependencies? : Record <string , string >
69 peerDependenciesMeta? : Record <string , { optional? : boolean }>
10+ optionalDependencies? : Record <string , string >
711}>()
812
13+ // Fetch outdated info for dependencies
14+ const outdatedDeps = useOutdatedDependencies (() => props .dependencies )
15+
16+ /**
17+ * Get CSS class for a dependency version based on outdated status
18+ */
19+ function getVersionClass(info : OutdatedDependencyInfo | undefined ): string {
20+ if (! info ) return ' text-fg-subtle'
21+
22+ // Red for major versions behind
23+ if (info .majorsBehind > 0 ) return ' text-red-500 cursor-help'
24+ // Orange for minor versions behind
25+ if (info .minorsBehind > 0 ) return ' text-orange-500 cursor-help'
26+ // Yellow for patch versions behind
27+ return ' text-yellow-500 cursor-help'
28+ }
29+
930// Expanded state for each section
1031const depsExpanded = ref (false )
1132const peerDepsExpanded = ref (false )
33+ const optionalDepsExpanded = ref (false )
1234
1335// Sort dependencies alphabetically
1436const sortedDependencies = computed (() => {
@@ -32,27 +54,21 @@ const sortedPeerDependencies = computed(() => {
3254 return a .name .localeCompare (b .name )
3355 })
3456})
57+
58+ // Sort optional dependencies alphabetically
59+ const sortedOptionalDependencies = computed (() => {
60+ if (! props .optionalDependencies ) return []
61+ return Object .entries (props .optionalDependencies ).sort (([a ], [b ]) => a .localeCompare (b ))
62+ })
3563 </script >
3664
3765<template >
3866 <div class =" space-y-8" >
3967 <!-- Dependencies -->
4068 <section v-if =" sortedDependencies.length > 0" aria-labelledby =" dependencies-heading" >
41- <div class =" flex items-center justify-between mb-3" >
42- <h2 id =" dependencies-heading" class =" text-xs text-fg-subtle uppercase tracking-wider" >
43- Dependencies ({{ sortedDependencies.length }})
44- </h2 >
45- <a
46- :href =" `https://npmgraph.js.org/?q=${packageName}`"
47- target =" _blank"
48- rel =" noopener noreferrer"
49- class =" link-subtle text-fg-subtle"
50- aria-label =" View dependency graph"
51- title =" View dependency graph"
52- >
53- <span class =" text-xs uppercase tracking-wider" > Graph </span >
54- </a >
55- </div >
69+ <h2 id =" dependencies-heading" class =" text-xs text-fg-subtle uppercase tracking-wider mb-3" >
70+ Dependencies ({{ sortedDependencies.length }})
71+ </h2 >
5672 <ul class =" space-y-1 list-none m-0 p-0" aria-label =" Package dependencies" >
5773 <li
5874 v-for =" [dep, version] in sortedDependencies.slice(0, depsExpanded ? undefined : 10)"
@@ -65,11 +81,26 @@ const sortedPeerDependencies = computed(() => {
6581 >
6682 {{ dep }}
6783 </NuxtLink >
68- <span
69- class =" font-mono text-xs text-fg-subtle max-w-[50%] text-right truncate"
70- :title =" version"
71- >
72- {{ version }}
84+ <span class =" flex items-center gap-1" >
85+ <span
86+ v-if =" outdatedDeps[dep]"
87+ class =" shrink-0"
88+ :class =" getVersionClass(outdatedDeps[dep])"
89+ :title =" getOutdatedTooltip(outdatedDeps[dep])"
90+ aria-hidden =" true"
91+ >
92+ <span class =" i-carbon-warning-alt w-3 h-3" />
93+ </span >
94+ <span
95+ class =" font-mono text-xs text-right truncate"
96+ :class =" getVersionClass(outdatedDeps[dep])"
97+ :title =" outdatedDeps[dep] ? getOutdatedTooltip(outdatedDeps[dep]) : version"
98+ >
99+ {{ version }}
100+ </span >
101+ <span v-if =" outdatedDeps[dep]" class =" sr-only" >
102+ ({{ getOutdatedTooltip(outdatedDeps[dep]) }})
103+ </span >
73104 </span >
74105 </li >
75106 </ul >
@@ -129,5 +160,49 @@ const sortedPeerDependencies = computed(() => {
129160 show all {{ sortedPeerDependencies.length }} peer deps
130161 </button >
131162 </section >
163+
164+ <!-- Optional Dependencies -->
165+ <section
166+ v-if =" sortedOptionalDependencies.length > 0"
167+ aria-labelledby =" optional-dependencies-heading"
168+ >
169+ <h2
170+ id =" optional-dependencies-heading"
171+ class =" text-xs text-fg-subtle uppercase tracking-wider mb-3"
172+ >
173+ Optional Dependencies ({{ sortedOptionalDependencies.length }})
174+ </h2 >
175+ <ul class =" space-y-1 list-none m-0 p-0" aria-label =" Package optional dependencies" >
176+ <li
177+ v-for =" [dep, version] in sortedOptionalDependencies.slice(
178+ 0,
179+ optionalDepsExpanded ? undefined : 10,
180+ )"
181+ :key =" dep"
182+ class =" flex items-center justify-between py-1 text-sm gap-2"
183+ >
184+ <NuxtLink
185+ :to =" { name: 'package', params: { package: dep.split('/') } }"
186+ class =" font-mono text-fg-muted hover:text-fg transition-colors duration-200 truncate min-w-0"
187+ >
188+ {{ dep }}
189+ </NuxtLink >
190+ <span
191+ class =" font-mono text-xs text-fg-subtle max-w-[50%] text-right truncate"
192+ :title =" version"
193+ >
194+ {{ version }}
195+ </span >
196+ </li >
197+ </ul >
198+ <button
199+ v-if =" sortedOptionalDependencies.length > 10 && !optionalDepsExpanded"
200+ type =" button"
201+ class =" mt-2 font-mono text-xs text-fg-muted hover:text-fg transition-colors duration-200 rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
202+ @click =" optionalDepsExpanded = true"
203+ >
204+ show all {{ sortedOptionalDependencies.length }} optional deps
205+ </button >
206+ </section >
132207 </div >
133208</template >
0 commit comments