11<script setup lang="ts">
22import { joinURL } from ' ufo'
33import type { PackumentVersion , NpmVersionDist } from ' #shared/types'
4+ import type { JsrPackageInfo } from ' #shared/types/jsr'
45
56definePageMeta ({
67 name: ' package' ,
@@ -59,6 +60,13 @@ const { data: readmeData } = useLazyFetch<{ html: string }>(
5960 { default : () => ({ html: ' ' }) },
6061)
6162
63+ // Check if package exists on JSR (only for scoped packages)
64+ const { data : jsrInfo } = useLazyFetch <JsrPackageInfo >(() => ` /api/jsr/${packageName .value } ` , {
65+ default : () => ({ exists: false }),
66+ // Only fetch for scoped packages (JSR requirement)
67+ immediate: computed (() => packageName .value .startsWith (' @' )).value ,
68+ })
69+
6270// Get the version to display (requested or latest)
6371const displayVersion = computed (() => {
6472 if (! pkg .value ) return null
@@ -143,18 +151,7 @@ function hasProvenance(version: PackumentVersion | null): boolean {
143151 return !! dist .attestations
144152}
145153
146- // Package manager install commands
147- const packageManagers = [
148- { id: ' npm' , label: ' npm' , action: ' install' },
149- { id: ' pnpm' , label: ' pnpm' , action: ' add' },
150- { id: ' yarn' , label: ' yarn' , action: ' add' },
151- { id: ' bun' , label: ' bun' , action: ' add' },
152- { id: ' deno' , label: ' deno' , action: ' add npm:' },
153- ] as const
154-
155- type PackageManagerId = (typeof packageManagers )[number ][' id' ]
156-
157- // Persist preference in localStorage
154+ // Persist package manager preference in localStorage
158155const selectedPM = ref <PackageManagerId >(' npm' )
159156
160157onMounted (() => {
@@ -168,24 +165,24 @@ watch(selectedPM, value => {
168165 localStorage .setItem (' npmx-pm' , value )
169166})
170167
171- const currentPM = computed (
172- () => packageManagers .find (p => p .id === selectedPM .value ) || packageManagers [0 ],
173- )
174- const selectedPMLabel = computed (() => currentPM .value .label )
175- const selectedPMAction = computed (() => currentPM .value .action )
168+ const installCommandParts = computed (() => {
169+ if (! pkg .value ) return []
170+ return getInstallCommandParts ({
171+ packageName: pkg .value .name ,
172+ packageManager: selectedPM .value ,
173+ version: requestedVersion .value ,
174+ jsrInfo: jsrInfo .value ,
175+ })
176+ })
176177
177178const installCommand = computed (() => {
178179 if (! pkg .value ) return ' '
179- const pm = currentPM .value
180- let command = ` ${pm .label } ${pm .action } ${pkg .value .name } `
181- // deno uses "add npm:package" format
182- if (pm .id === ' deno' ) {
183- command = ` ${pm .label } ${pm .action }${pkg .value .name } `
184- }
185- if (requestedVersion .value ) {
186- command += ` @${requestedVersion .value } `
187- }
188- return command
180+ return getInstallCommand ({
181+ packageName: pkg .value .name ,
182+ packageManager: selectedPM .value ,
183+ version: requestedVersion .value ,
184+ jsrInfo: jsrInfo .value ,
185+ })
189186})
190187
191188// Copy install command
@@ -415,6 +412,18 @@ defineOgImageComponent('Package', {
415412 npm
416413 </a >
417414 </li >
415+ <li v-if =" jsrInfo?.exists && jsrInfo.url" >
416+ <a
417+ :href =" jsrInfo.url"
418+ target =" _blank"
419+ rel =" noopener noreferrer"
420+ class =" link-subtle font-mono text-sm inline-flex items-center gap-1.5"
421+ title =" Also available on JSR"
422+ >
423+ <span class =" i-simple-icons-jsr w-4 h-4" />
424+ jsr
425+ </a >
426+ </li >
418427 <li >
419428 <a
420429 :href =" `https://socket.dev/npm/package/${pkg.name}/overview/${displayVersion?.version ?? 'latest'}`"
@@ -517,16 +526,14 @@ defineOgImageComponent('Package', {
517526 <span class =" text-fg-subtle font-mono text-sm select-none" >$</span >
518527 <code class =" font-mono text-sm"
519528 ><ClientOnly
520- ><span class =" text-fg" >{{ selectedPMLabel }}</span
521- >  ; <span class =" text-fg-muted" >{{ selectedPMAction }}</span
522- ><span v-if =" selectedPM !== 'deno'" class =" text-fg-muted"
523- >  ; {{ pkg.name }}</span
524- ><span v-else class =" text-fg-muted" >{{ pkg.name }}</span
525- ><span v-if =" requestedVersion" class =" text-fg-muted" >@{{ requestedVersion }}</span
529+ ><span
530+ v-for =" (part, i) in installCommandParts"
531+ :key =" i"
532+ :class =" i === 0 ? 'text-fg' : 'text-fg-muted'"
533+ >{{ i > 0 ? ' ' : '' }}{{ part }}</span
526534 ><template #fallback
527- ><span class =" text-fg" >npm</span >  ; <span class =" text-fg-muted"
528- >install  ; {{ pkg.name }}</span
529- ></template
535+ ><span class =" text-fg" >npm</span
536+ ><span class =" text-fg-muted" > install {{ pkg.name }}</span ></template
530537 ></ClientOnly
531538 ></code
532539 >
0 commit comments