forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1import type { JsrPackageInfo } from '#shared/types/jsr'
2import { getCreateShortName } from '#shared/utils/package-analysis'
3
4// @unocss-include
5export const packageManagers = [
6 {
7 id: 'npm',
8 label: 'npm',
9 action: 'install',
10 executeLocal: 'npx',
11 executeRemote: 'npx',
12 create: 'npm create',
13 icon: 'i-simple-icons:npm',
14 },
15 {
16 id: 'pnpm',
17 label: 'pnpm',
18 action: 'add',
19 executeLocal: 'pnpm exec',
20 executeRemote: 'pnpm dlx',
21 create: 'pnpm create',
22 icon: 'i-simple-icons:pnpm',
23 },
24 {
25 id: 'yarn',
26 label: 'yarn',
27 action: 'add',
28 // For both yarn v1 and v2+ support
29 // local exec defers to npx instead
30 executeLocal: 'npx',
31 executeRemote: 'yarn dlx',
32 create: 'yarn create',
33 icon: 'i-simple-icons:yarn',
34 },
35 {
36 id: 'bun',
37 label: 'bun',
38 action: 'add',
39 executeLocal: 'bunx',
40 executeRemote: 'bunx',
41 create: 'bun create',
42 icon: 'i-simple-icons:bun',
43 },
44 {
45 id: 'deno',
46 label: 'deno',
47 action: 'add',
48 executeLocal: 'deno run',
49 executeRemote: 'deno run',
50 create: 'deno run',
51 icon: 'i-simple-icons:deno',
52 },
53 {
54 id: 'vlt',
55 label: 'vlt',
56 action: 'install',
57 executeLocal: 'vlx',
58 executeRemote: 'vlx',
59 create: 'vlx',
60 icon: 'i-custom-vlt',
61 },
62] as const
63
64export type PackageManagerId = (typeof packageManagers)[number]['id']
65
66export interface InstallCommandOptions {
67 packageName: string
68 packageManager: PackageManagerId
69 version?: string | null
70 jsrInfo?: JsrPackageInfo | null
71}
72
73/**
74 * Get the package specifier for a given package manager.
75 * Handles jsr: prefix for deno (when available on JSR).
76 */
77export function getPackageSpecifier(options: InstallCommandOptions): string {
78 const { packageName, packageManager, jsrInfo } = options
79
80 if (packageManager === 'deno') {
81 if (jsrInfo?.exists && jsrInfo.scope && jsrInfo.name) {
82 // Native JSR package: jsr:@scope/name
83 return `jsr:@${jsrInfo.scope}/${jsrInfo.name}`
84 }
85 // npm compatibility: npm:package
86 return `npm:${packageName}`
87 }
88
89 // Standard package managers (npm, pnpm, yarn, bun, vlt)
90 return packageName
91}
92
93/**
94 * Generate the full install command for a package.
95 */
96export function getInstallCommand(options: InstallCommandOptions): string {
97 return getInstallCommandParts(options).join(' ')
98}
99
100/**
101 * Generate install command as an array of parts.
102 * First element is the command (e.g., "npm"), rest are arguments.
103 * Useful for rendering with different styling for command vs args.
104 */
105export function getInstallCommandParts(options: InstallCommandOptions): string[] {
106 const pm = packageManagers.find(p => p.id === options.packageManager)
107 if (!pm) return []
108
109 const spec = getPackageSpecifier(options)
110 const version = options.version ? `@${options.version}` : ''
111
112 return [pm.label, pm.action, `${spec}${version}`]
113}
114
115export interface ExecuteCommandOptions extends InstallCommandOptions {
116 /** Whether this is a binary-only package (download & run vs local run) */
117 isBinaryOnly?: boolean
118 /** Whether this is a create-* package (uses shorthand create command) */
119 isCreatePackage?: boolean
120}
121
122export function getExecuteCommand(options: ExecuteCommandOptions): string {
123 return getExecuteCommandParts(options).join(' ')
124}
125
126export function getExecuteCommandParts(options: ExecuteCommandOptions): string[] {
127 const pm = packageManagers.find(p => p.id === options.packageManager)
128 if (!pm) return []
129
130 // For create-* packages, use the shorthand create command
131 if (options.isCreatePackage) {
132 const shortName = getCreateShortName(options.packageName)
133 if (shortName !== options.packageName) {
134 return [...pm.create.split(' '), shortName]
135 }
136 }
137
138 // Choose remote or local execute based on package type
139 const executeCmd = options.isBinaryOnly ? pm.executeRemote : pm.executeLocal
140 return [...executeCmd.split(' '), getPackageSpecifier(options)]
141}