[READ-ONLY] a fast, modern browser for the npm registry
at main 147 lines 4.2 kB view raw
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 dev?: boolean 72} 73 74export function getDevDependencyFlag(packageManager: PackageManagerId): '-D' | '-d' { 75 return packageManager === 'bun' ? '-d' : '-D' 76} 77 78/** 79 * Get the package specifier for a given package manager. 80 * Handles jsr: prefix for deno (when available on JSR). 81 */ 82export function getPackageSpecifier(options: InstallCommandOptions): string { 83 const { packageName, packageManager, jsrInfo } = options 84 85 if (packageManager === 'deno') { 86 if (jsrInfo?.exists && jsrInfo.scope && jsrInfo.name) { 87 // Native JSR package: jsr:@scope/name 88 return `jsr:@${jsrInfo.scope}/${jsrInfo.name}` 89 } 90 // npm compatibility: npm:package 91 return `npm:${packageName}` 92 } 93 94 // Standard package managers (npm, pnpm, yarn, bun, vlt) 95 return packageName 96} 97 98/** 99 * Generate the full install command for a package. 100 */ 101export function getInstallCommand(options: InstallCommandOptions): string { 102 return getInstallCommandParts(options).join(' ') 103} 104 105/** 106 * Generate install command as an array of parts. 107 * First element is the command (e.g., "npm"), rest are arguments. 108 * Useful for rendering with different styling for command vs args. 109 */ 110export function getInstallCommandParts(options: InstallCommandOptions): string[] { 111 const pm = packageManagers.find(p => p.id === options.packageManager) 112 if (!pm) return [] 113 114 const spec = getPackageSpecifier(options) 115 const version = options.version ? `@${options.version}` : '' 116 const devFlag = options.dev ? [getDevDependencyFlag(options.packageManager)] : [] 117 118 return [pm.label, pm.action, ...devFlag, `${spec}${version}`] 119} 120 121export interface ExecuteCommandOptions extends InstallCommandOptions { 122 /** Whether this is a binary-only package (download & run vs local run) */ 123 isBinaryOnly?: boolean 124 /** Whether this is a create-* package (uses shorthand create command) */ 125 isCreatePackage?: boolean 126} 127 128export function getExecuteCommand(options: ExecuteCommandOptions): string { 129 return getExecuteCommandParts(options).join(' ') 130} 131 132export function getExecuteCommandParts(options: ExecuteCommandOptions): string[] { 133 const pm = packageManagers.find(p => p.id === options.packageManager) 134 if (!pm) return [] 135 136 // For create-* packages, use the shorthand create command 137 if (options.isCreatePackage) { 138 const shortName = getCreateShortName(options.packageName) 139 if (shortName !== options.packageName) { 140 return [...pm.create.split(' '), shortName] 141 } 142 } 143 144 // Choose remote or local execute based on package type 145 const executeCmd = options.isBinaryOnly ? pm.executeRemote : pm.executeLocal 146 return [...executeCmd.split(' '), getPackageSpecifier(options)] 147}