[READ-ONLY] a fast, modern browser for the npm registry
at main 121 lines 3.3 kB view raw
1import * as v from 'valibot' 2import { PackageRouteParamsSchema } from '#shared/schemas/package' 3import { 4 CACHE_MAX_AGE_ONE_HOUR, 5 NPM_MISSING_README_SENTINEL, 6 NPM_README_TRUNCATION_THRESHOLD, 7} from '#shared/utils/constants' 8 9/** Standard README filenames to try when fetching from jsdelivr (case-sensitive CDN) */ 10const standardReadmeFilenames = [ 11 'README.md', 12 'readme.md', 13 'Readme.md', 14 'README', 15 'readme', 16 'README.markdown', 17 'readme.markdown', 18] 19 20/** Matches standard README filenames (case-insensitive, for checking registry metadata) */ 21const standardReadmePattern = /^readme(?:\.md|\.markdown)?$/i 22 23export function isStandardReadme(filename: string | undefined): boolean { 24 return !!filename && standardReadmePattern.test(filename) 25} 26 27/** 28 * Fetch README from jsdelivr CDN for a specific package version. 29 * Falls back through common README filenames. 30 */ 31export async function fetchReadmeFromJsdelivr( 32 packageName: string, 33 readmeFilenames: string[], 34 version?: string, 35): Promise<string | null> { 36 const versionSuffix = version ? `@${version}` : '' 37 38 for (const filename of readmeFilenames) { 39 try { 40 const url = `https://cdn.jsdelivr.net/npm/${packageName}${versionSuffix}/${filename}` 41 const response = await fetch(url) 42 if (response.ok) { 43 return await response.text() 44 } 45 } catch { 46 // Try next filename 47 } 48 } 49 50 return null 51} 52 53export const resolvePackageReadmeSource = defineCachedFunction( 54 async (packagePath: string) => { 55 const pkgParamSegments = packagePath.split('/') 56 57 const { rawPackageName, rawVersion } = parsePackageParams(pkgParamSegments) 58 59 const { packageName, version } = v.parse(PackageRouteParamsSchema, { 60 packageName: rawPackageName, 61 version: rawVersion, 62 }) 63 64 const packageData = await fetchNpmPackage(packageName) 65 66 let readmeContent: string | undefined 67 let readmeFilename: string | undefined 68 69 if (version) { 70 const versionData = packageData.versions[version] 71 if (versionData) { 72 readmeContent = versionData.readme 73 readmeFilename = versionData.readmeFilename 74 } 75 } else { 76 readmeContent = packageData.readme 77 readmeFilename = packageData.readmeFilename 78 } 79 80 const hasValidNpmReadme = readmeContent && readmeContent !== NPM_MISSING_README_SENTINEL 81 82 if ( 83 !hasValidNpmReadme || 84 !isStandardReadme(readmeFilename) || 85 readmeContent!.length >= NPM_README_TRUNCATION_THRESHOLD 86 ) { 87 const resolvedVersion = version ?? packageData['dist-tags']?.latest 88 const jsdelivrReadme = await fetchReadmeFromJsdelivr( 89 packageName, 90 standardReadmeFilenames, 91 resolvedVersion, 92 ) 93 if (jsdelivrReadme) { 94 readmeContent = jsdelivrReadme 95 } 96 } 97 98 if (!readmeContent || readmeContent === NPM_MISSING_README_SENTINEL) { 99 return { 100 packageName, 101 version, 102 markdown: undefined, 103 repoInfo: undefined, 104 } 105 } 106 107 const repoInfo = parseRepositoryInfo(packageData.repository) 108 109 return { 110 packageName, 111 version, 112 markdown: readmeContent, 113 repoInfo, 114 } 115 }, 116 { 117 maxAge: CACHE_MAX_AGE_ONE_HOUR, 118 swr: true, 119 getKey: (packagePath: string) => packagePath, 120 }, 121)