forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1import type { JsrPackageInfo } from '#shared/types/jsr'
2
3/**
4 * Composable for generating install commands with support for
5 * multiple package managers, @types packages, and JSR.
6 */
7export function useInstallCommand(
8 packageName: MaybeRefOrGetter<string | null>,
9 requestedVersion: MaybeRefOrGetter<string | null>,
10 jsrInfo: MaybeRefOrGetter<JsrPackageInfo | null>,
11 typesPackageName: MaybeRefOrGetter<string | null>,
12 installVersionOverride?: MaybeRefOrGetter<string | null>,
13) {
14 const selectedPM = useSelectedPackageManager()
15 const { settings } = useSettings()
16
17 // Check if we should show @types in install command
18 const showTypesInInstall = computed(() => {
19 return settings.value.includeTypesInInstall && !!toValue(typesPackageName)
20 })
21
22 const installCommandParts = computed(() => {
23 const name = toValue(packageName)
24 if (!name) return []
25 const version = toValue(installVersionOverride) ?? toValue(requestedVersion)
26 return getInstallCommandParts({
27 packageName: name,
28 packageManager: selectedPM.value,
29 version,
30 jsrInfo: toValue(jsrInfo),
31 })
32 })
33
34 const installCommand = computed(() => {
35 const name = toValue(packageName)
36 if (!name) return ''
37 const version = toValue(installVersionOverride) ?? toValue(requestedVersion)
38 return getInstallCommand({
39 packageName: name,
40 packageManager: selectedPM.value,
41 version,
42 jsrInfo: toValue(jsrInfo),
43 })
44 })
45
46 // Get the dev dependency flag for the selected package manager
47 const devFlag = computed(() => {
48 // bun uses lowercase -d, all others use -D
49 return selectedPM.value === 'bun' ? '-d' : '-D'
50 })
51
52 // @types install command parts (for display)
53 const typesInstallCommandParts = computed(() => {
54 const types = toValue(typesPackageName)
55 if (!types) return []
56 const pm = packageManagers.find(p => p.id === selectedPM.value)
57 if (!pm) return []
58
59 const pkgSpec = selectedPM.value === 'deno' ? `npm:${types}` : types
60
61 return [pm.label, pm.action, devFlag.value, pkgSpec]
62 })
63
64 // Full install command including @types (for copying)
65 const fullInstallCommand = computed(() => {
66 if (!installCommand.value) return ''
67 const types = toValue(typesPackageName)
68 if (!showTypesInInstall.value || !types) {
69 return installCommand.value
70 }
71
72 const pm = packageManagers.find(p => p.id === selectedPM.value)
73 if (!pm) return installCommand.value
74
75 const pkgSpec = selectedPM.value === 'deno' ? `npm:${types}` : types
76
77 // Use semicolon to separate commands
78 return `${installCommand.value}; ${pm.label} ${pm.action} ${devFlag.value} ${pkgSpec}`
79 })
80
81 // Copy state
82 const { copied, copy } = useClipboard({ copiedDuring: 2000 })
83
84 async function copyInstallCommand() {
85 if (!fullInstallCommand.value) return
86 await copy(fullInstallCommand.value)
87 }
88
89 return {
90 selectedPM,
91 installCommandParts,
92 installCommand,
93 typesInstallCommandParts,
94 fullInstallCommand,
95 showTypesInInstall,
96 copied,
97 copyInstallCommand,
98 }
99}