[READ-ONLY] a fast, modern browser for the npm registry
at main 110 lines 2.8 kB view raw
1export type DevDependencySuggestionReason = 'known-package' | 'readme-hint' 2 3export interface DevDependencySuggestion { 4 recommended: boolean 5 reason?: DevDependencySuggestionReason 6} 7 8const KNOWN_DEV_DEPENDENCY_PACKAGES = new Set<string>([ 9 'biome', 10 'chai', 11 'eslint', 12 'esbuild', 13 'husky', 14 'jest', 15 'lint-staged', 16 'mocha', 17 'oxc', 18 'oxfmt', 19 'oxlint', 20 'playwright', 21 'prettier', 22 'rolldown', 23 'rollup', 24 'stylelint', 25 'ts-jest', 26 'ts-node', 27 'tsx', 28 'turbo', 29 'typescript', 30 'vite', 31 'vitest', 32 'webpack', 33]) 34 35const KNOWN_DEV_DEPENDENCY_PACKAGE_PREFIXES = [ 36 '@typescript-eslint/', 37 'eslint-', 38 'prettier-', 39 'vite-', 40 'webpack-', 41 'babel-', 42] 43 44function isKnownDevDependencyPackage(packageName: string): boolean { 45 const normalized = packageName.toLowerCase() 46 if (normalized.startsWith('@types/')) { 47 return true 48 } 49 // Match scoped packages by name segment, e.g. @scope/eslint-config 50 const namePart = normalized.includes('/') ? normalized.split('/').pop() : normalized 51 if (!namePart) return false 52 53 return ( 54 KNOWN_DEV_DEPENDENCY_PACKAGES.has(normalized) || 55 KNOWN_DEV_DEPENDENCY_PACKAGES.has(namePart) || 56 KNOWN_DEV_DEPENDENCY_PACKAGE_PREFIXES.some(prefix => 57 prefix.startsWith('@') ? normalized.startsWith(prefix) : namePart.startsWith(prefix), 58 ) 59 ) 60} 61 62function escapeRegExp(text: string): string { 63 return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') 64} 65 66function hasReadmeDevInstallHint(packageName: string, readmeContent?: string | null): boolean { 67 if (!readmeContent) return false 68 69 const escapedName = escapeRegExp(packageName) 70 const escapedNpmName = escapeRegExp(`npm:${packageName}`) 71 const packageSpec = `(?:${escapedName}|${escapedNpmName})(?:@[\\w.-]+)?` 72 73 const patterns = [ 74 // npm install -D pkg / pnpm add --save-dev pkg 75 new RegExp( 76 String.raw`(?:npm|pnpm|yarn|bun|vlt)\s+(?:install|add|i)\s+(?:--save-dev|--dev|-d)\s+${packageSpec}`, 77 'i', 78 ), 79 // npm install pkg --save-dev / pnpm add pkg -D 80 new RegExp( 81 String.raw`(?:npm|pnpm|yarn|bun|vlt)\s+(?:install|add|i)\s+${packageSpec}\s+(?:--save-dev|--dev|-d)`, 82 'i', 83 ), 84 // deno add -D npm:pkg 85 new RegExp(String.raw`deno\s+add\s+(?:--dev|-D)\s+${packageSpec}`, 'i'), 86 ] 87 88 return patterns.some(pattern => pattern.test(readmeContent)) 89} 90 91export function getDevDependencySuggestion( 92 packageName: string, 93 readmeContent?: string | null, 94): DevDependencySuggestion { 95 if (isKnownDevDependencyPackage(packageName)) { 96 return { 97 recommended: true, 98 reason: 'known-package', 99 } 100 } 101 102 if (hasReadmeDevInstallHint(packageName, readmeContent)) { 103 return { 104 recommended: true, 105 reason: 'readme-hint', 106 } 107 } 108 109 return { recommended: false } 110}