forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1<script setup lang="ts">
2import { getOutdatedTooltip, getVersionClass } from '~/utils/npm/outdated-dependencies'
3import type { RouteLocationRaw } from 'vue-router'
4
5const props = defineProps<{
6 packageName: string
7 version: string
8 installScripts: {
9 scripts: ('preinstall' | 'install' | 'postinstall')[]
10 content?: Record<string, string>
11 npxDependencies: Record<string, string>
12 }
13}>()
14
15function getCodeLink(filePath: string): RouteLocationRaw {
16 const split = props.packageName.split('/')
17
18 return {
19 name: 'code',
20 params: {
21 org: split.length === 2 ? split[0] : null,
22 packageName: split.length === 2 ? split[1]! : split[0]!,
23 version: props.version,
24 filePath: filePath,
25 },
26 }
27}
28
29const scriptParts = computed(() => {
30 const parts: Record<
31 string,
32 { prefix: string | null; filePath: string | null; link: RouteLocationRaw }
33 > = {}
34 for (const scriptName of props.installScripts.scripts) {
35 const content = props.installScripts.content?.[scriptName]
36 if (!content) continue
37 const parsed = parseNodeScript(content)
38 if (parsed) {
39 parts[scriptName] = {
40 prefix: parsed.prefix,
41 filePath: parsed.filePath,
42 link: getCodeLink(parsed.filePath),
43 }
44 } else {
45 parts[scriptName] = { prefix: null, filePath: null, link: getCodeLink('package.json') }
46 }
47 }
48 return parts
49})
50
51const outdatedNpxDeps = useOutdatedDependencies(() => props.installScripts.npxDependencies)
52const hasNpxDeps = computed(() => Object.keys(props.installScripts.npxDependencies).length > 0)
53const sortedNpxDeps = computed(() => {
54 return Object.entries(props.installScripts.npxDependencies).sort(([a], [b]) => a.localeCompare(b))
55})
56
57const isExpanded = shallowRef(false)
58</script>
59
60<template>
61 <CollapsibleSection
62 :title="$t('package.install_scripts.title')"
63 id="installScripts"
64 icon="i-lucide:circle-alert w-3 h-3 text-yellow-500"
65 >
66 <!-- Script list: name as label, content below -->
67 <dl class="space-y-2 m-0">
68 <div v-for="scriptName in installScripts.scripts" :key="scriptName">
69 <dt class="font-mono text-xs text-fg-muted">{{ scriptName }}</dt>
70 <dd
71 class="font-mono text-sm text-fg-subtle m-0 truncate"
72 :title="installScripts.content?.[scriptName]"
73 >
74 <template v-if="installScripts.content?.[scriptName] && scriptParts[scriptName]">
75 <template v-if="scriptParts[scriptName].prefix">
76 {{ scriptParts[scriptName].prefix
77 }}<LinkBase :to="scriptParts[scriptName].link">{{
78 scriptParts[scriptName].filePath
79 }}</LinkBase>
80 </template>
81 <LinkBase v-else :to="scriptParts[scriptName].link">
82 {{ installScripts.content[scriptName] }}
83 </LinkBase>
84 </template>
85 <span v-else tabindex="0" class="cursor-help">
86 {{ $t('package.install_scripts.script_label') }}
87 </span>
88 </dd>
89 </div>
90 </dl>
91
92 <!-- npx packages (expandable) -->
93 <div v-if="hasNpxDeps" class="mt-3">
94 <button
95 type="button"
96 class="flex items-center gap-1.5 text-xs text-fg-muted hover:text-fg transition-colors duration-200 focus-visible:outline-accent/70 rounded"
97 :aria-expanded="isExpanded"
98 aria-controls="npx-packages-details"
99 @click="isExpanded = !isExpanded"
100 >
101 <span
102 class="i-lucide:chevron-right rtl-flip w-3 h-3 transition-transform duration-200"
103 :class="{ 'rotate-90': isExpanded }"
104 aria-hidden="true"
105 />
106 {{
107 $t(
108 'package.install_scripts.npx_packages',
109 { count: sortedNpxDeps.length },
110 sortedNpxDeps.length,
111 )
112 }}
113 </button>
114
115 <ul
116 v-show="isExpanded"
117 id="npx-packages-details"
118 class="mt-2 space-y-1 list-none m-0 p-0 ps-4"
119 >
120 <li
121 v-for="[dep, version] in sortedNpxDeps"
122 :key="dep"
123 class="flex items-center justify-between py-0.5 text-sm gap-2"
124 >
125 <LinkBase
126 :to="packageRoute(dep)"
127 class="font-mono text-fg-muted hover:text-fg transition-colors duration-200 truncate min-w-0"
128 >
129 {{ dep }}
130 </LinkBase>
131 <span class="flex items-center gap-1">
132 <TooltipApp
133 v-if="
134 outdatedNpxDeps[dep] &&
135 outdatedNpxDeps[dep].resolved !== outdatedNpxDeps[dep].latest
136 "
137 class="shrink-0 p-2 -m-2"
138 :class="getVersionClass(outdatedNpxDeps[dep])"
139 aria-hidden="true"
140 :text="getOutdatedTooltip(outdatedNpxDeps[dep], $t)"
141 >
142 <span class="i-lucide:circle-alert w-3 h-3" />
143 </TooltipApp>
144 <span
145 class="font-mono text-xs text-end truncate"
146 :class="getVersionClass(outdatedNpxDeps[dep])"
147 :title="
148 outdatedNpxDeps[dep]
149 ? outdatedNpxDeps[dep].resolved === outdatedNpxDeps[dep].latest
150 ? $t('package.install_scripts.currently', {
151 version: outdatedNpxDeps[dep].latest,
152 })
153 : getOutdatedTooltip(outdatedNpxDeps[dep], $t)
154 : version
155 "
156 >
157 {{ version }}
158 </span>
159 </span>
160 </li>
161 </ul>
162 </div>
163 </CollapsibleSection>
164</template>