Skip to content

Commit 323f548

Browse files
committed
refactor: extract + test utils
1 parent aacf485 commit 323f548

3 files changed

Lines changed: 375 additions & 51 deletions

File tree

app/pages/[...package].vue

Lines changed: 22 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -151,19 +151,7 @@ function hasProvenance(version: PackumentVersion | null): boolean {
151151
return !!dist.attestations
152152
}
153153
154-
// Package manager install commands
155-
const packageManagers = [
156-
{ id: 'npm', label: 'npm', action: 'install' },
157-
{ id: 'pnpm', label: 'pnpm', action: 'add' },
158-
{ id: 'yarn', label: 'yarn', action: 'add' },
159-
{ id: 'bun', label: 'bun', action: 'add' },
160-
{ id: 'deno', label: 'deno', action: 'add' },
161-
{ id: 'jsr', label: 'jsr', action: 'add' },
162-
] as const
163-
164-
type PackageManagerId = (typeof packageManagers)[number]['id']
165-
166-
// Persist preference in localStorage
154+
// Persist package manager preference in localStorage
167155
const selectedPM = ref<PackageManagerId>('npm')
168156
169157
onMounted(() => {
@@ -177,41 +165,24 @@ watch(selectedPM, value => {
177165
localStorage.setItem('npmx-pm', value)
178166
})
179167
180-
const currentPM = computed(
181-
() => packageManagers.find(p => p.id === selectedPM.value) || packageManagers[0],
182-
)
183-
const selectedPMLabel = computed(() => currentPM.value.label)
184-
const selectedPMAction = computed(() => currentPM.value.action)
185-
186-
// Get the package specifier for the current package manager
187-
const packageSpecifier = computed(() => {
188-
if (!pkg.value) return ''
189-
const pm = currentPM.value
190-
191-
if (pm.id === 'deno') {
192-
// deno add npm:package
193-
return `npm:${pkg.value.name}`
194-
}
195-
196-
if (pm.id === 'jsr') {
197-
if (jsrInfo.value?.exists && jsrInfo.value.scope && jsrInfo.value.name) {
198-
// Native JSR package: @scope/name
199-
return `@${jsrInfo.value.scope}/${jsrInfo.value.name}`
200-
}
201-
// npm compatibility: npm:package
202-
return `npm:${pkg.value.name}`
203-
}
204-
205-
// Standard package managers
206-
return pkg.value.name
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+
})
207176
})
208177
209178
const installCommand = computed(() => {
210179
if (!pkg.value) return ''
211-
const pm = currentPM.value
212-
const spec = packageSpecifier.value
213-
const version = requestedVersion.value ? `@${requestedVersion.value}` : ''
214-
return `${pm.label} ${pm.action} ${spec}${version}`
180+
return getInstallCommand({
181+
packageName: pkg.value.name,
182+
packageManager: selectedPM.value,
183+
version: requestedVersion.value,
184+
jsrInfo: jsrInfo.value,
185+
})
215186
})
216187
217188
// Copy install command
@@ -555,14 +526,14 @@ defineOgImageComponent('Package', {
555526
<span class="text-fg-subtle font-mono text-sm select-none">$</span>
556527
<code class="font-mono text-sm"
557528
><ClientOnly
558-
><span class="text-fg">{{ selectedPMLabel }}</span
559-
>&nbsp;<span class="text-fg-muted">{{ selectedPMAction }}</span
560-
>&nbsp;<span class="text-fg-muted">{{ packageSpecifier }}</span
561-
><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
562534
><template #fallback
563-
><span class="text-fg">npm</span>&nbsp;<span class="text-fg-muted"
564-
>install&nbsp;{{ pkg.name }}</span
565-
></template
535+
><span class="text-fg">npm</span
536+
><span class="text-fg-muted"> install {{ pkg.name }}</span></template
566537
></ClientOnly
567538
></code
568539
>

app/utils/install-command.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import type { JsrPackageInfo } from '#shared/types/jsr'
2+
3+
export const packageManagers = [
4+
{ id: 'npm', label: 'npm', action: 'install' },
5+
{ id: 'pnpm', label: 'pnpm', action: 'add' },
6+
{ id: 'yarn', label: 'yarn', action: 'add' },
7+
{ id: 'bun', label: 'bun', action: 'add' },
8+
{ id: 'deno', label: 'deno', action: 'add' },
9+
{ id: 'jsr', label: 'jsr', action: 'add' },
10+
] as const
11+
12+
export type PackageManagerId = (typeof packageManagers)[number]['id']
13+
14+
export interface InstallCommandOptions {
15+
packageName: string
16+
packageManager: PackageManagerId
17+
version?: string | null
18+
jsrInfo?: JsrPackageInfo | null
19+
}
20+
21+
/**
22+
* Get the package specifier for a given package manager.
23+
* Handles npm: prefix for deno and jsr (when not native).
24+
*/
25+
export function getPackageSpecifier(options: InstallCommandOptions): string {
26+
const { packageName, packageManager, jsrInfo } = options
27+
28+
if (packageManager === 'deno') {
29+
// deno add npm:package
30+
return `npm:${packageName}`
31+
}
32+
33+
if (packageManager === 'jsr') {
34+
if (jsrInfo?.exists && jsrInfo.scope && jsrInfo.name) {
35+
// Native JSR package: @scope/name
36+
return `@${jsrInfo.scope}/${jsrInfo.name}`
37+
}
38+
// npm compatibility: npm:package
39+
return `npm:${packageName}`
40+
}
41+
42+
// Standard package managers (npm, pnpm, yarn, bun)
43+
return packageName
44+
}
45+
46+
/**
47+
* Generate the full install command for a package.
48+
*/
49+
export function getInstallCommand(options: InstallCommandOptions): string {
50+
return getInstallCommandParts(options).join(' ')
51+
}
52+
53+
/**
54+
* Generate install command as an array of parts.
55+
* First element is the command (e.g., "npm"), rest are arguments.
56+
* Useful for rendering with different styling for command vs args.
57+
*/
58+
export function getInstallCommandParts(options: InstallCommandOptions): string[] {
59+
const pm = packageManagers.find(p => p.id === options.packageManager)
60+
if (!pm) return []
61+
62+
const spec = getPackageSpecifier(options)
63+
const version = options.version ? `@${options.version}` : ''
64+
65+
return [pm.label, pm.action, `${spec}${version}`]
66+
}

0 commit comments

Comments
 (0)