|
| 1 | +import type { |
| 2 | + Packument, |
| 3 | + NpmSearchResponse, |
| 4 | + NpmDownloadCount, |
| 5 | + NpmDownloadRange, |
| 6 | +} from '#shared/types' |
| 7 | + |
| 8 | +const NPM_REGISTRY = 'https://registry.npmjs.org' |
| 9 | +const NPM_API = 'https://api.npmjs.org' |
| 10 | + |
| 11 | +export function useNpmRegistry() { |
| 12 | + async function fetchPackage(name: string): Promise<Packument> { |
| 13 | + const encodedName = encodePackageName(name) |
| 14 | + return await $fetch<Packument>(`${NPM_REGISTRY}/${encodedName}`) |
| 15 | + } |
| 16 | + |
| 17 | + async function searchPackages( |
| 18 | + query: string, |
| 19 | + options: { |
| 20 | + size?: number |
| 21 | + from?: number |
| 22 | + quality?: number |
| 23 | + popularity?: number |
| 24 | + maintenance?: number |
| 25 | + } = {}, |
| 26 | + ): Promise<NpmSearchResponse> { |
| 27 | + const params = new URLSearchParams() |
| 28 | + params.set('text', query) |
| 29 | + if (options.size) params.set('size', String(options.size)) |
| 30 | + if (options.from) params.set('from', String(options.from)) |
| 31 | + if (options.quality !== undefined) params.set('quality', String(options.quality)) |
| 32 | + if (options.popularity !== undefined) params.set('popularity', String(options.popularity)) |
| 33 | + if (options.maintenance !== undefined) params.set('maintenance', String(options.maintenance)) |
| 34 | + |
| 35 | + return await $fetch<NpmSearchResponse>(`${NPM_REGISTRY}/-/v1/search?${params.toString()}`) |
| 36 | + } |
| 37 | + |
| 38 | + async function fetchDownloads( |
| 39 | + packageName: string, |
| 40 | + period: 'last-day' | 'last-week' | 'last-month' | 'last-year' = 'last-week', |
| 41 | + ): Promise<NpmDownloadCount> { |
| 42 | + const encodedName = encodePackageName(packageName) |
| 43 | + return await $fetch<NpmDownloadCount>(`${NPM_API}/downloads/point/${period}/${encodedName}`) |
| 44 | + } |
| 45 | + |
| 46 | + async function fetchDownloadRange( |
| 47 | + packageName: string, |
| 48 | + period: 'last-week' | 'last-month' | 'last-year' = 'last-month', |
| 49 | + ): Promise<NpmDownloadRange> { |
| 50 | + const encodedName = encodePackageName(packageName) |
| 51 | + return await $fetch<NpmDownloadRange>(`${NPM_API}/downloads/range/${period}/${encodedName}`) |
| 52 | + } |
| 53 | + |
| 54 | + return { |
| 55 | + fetchPackage, |
| 56 | + searchPackages, |
| 57 | + fetchDownloads, |
| 58 | + fetchDownloadRange, |
| 59 | + } |
| 60 | +} |
| 61 | + |
| 62 | +function encodePackageName(name: string): string { |
| 63 | + if (name.startsWith('@')) { |
| 64 | + return `@${encodeURIComponent(name.slice(1))}` |
| 65 | + } |
| 66 | + return encodeURIComponent(name) |
| 67 | +} |
| 68 | + |
| 69 | +export function usePackage(name: MaybeRefOrGetter<string>) { |
| 70 | + const registry = useNpmRegistry() |
| 71 | + |
| 72 | + return useAsyncData( |
| 73 | + `package:${toValue(name)}`, |
| 74 | + () => registry.fetchPackage(toValue(name)), |
| 75 | + { watch: [() => toValue(name)] }, |
| 76 | + ) |
| 77 | +} |
| 78 | + |
| 79 | +export function usePackageDownloads( |
| 80 | + name: MaybeRefOrGetter<string>, |
| 81 | + period: MaybeRefOrGetter<'last-day' | 'last-week' | 'last-month' | 'last-year'> = 'last-week', |
| 82 | +) { |
| 83 | + const registry = useNpmRegistry() |
| 84 | + |
| 85 | + return useAsyncData( |
| 86 | + `downloads:${toValue(name)}:${toValue(period)}`, |
| 87 | + () => registry.fetchDownloads(toValue(name), toValue(period)), |
| 88 | + { watch: [() => toValue(name), () => toValue(period)] }, |
| 89 | + ) |
| 90 | +} |
| 91 | + |
| 92 | +export function useNpmSearch( |
| 93 | + query: MaybeRefOrGetter<string>, |
| 94 | + options: MaybeRefOrGetter<{ |
| 95 | + size?: number |
| 96 | + from?: number |
| 97 | + }> = {}, |
| 98 | +) { |
| 99 | + const registry = useNpmRegistry() |
| 100 | + |
| 101 | + return useAsyncData( |
| 102 | + `search:${toValue(query)}:${JSON.stringify(toValue(options))}`, |
| 103 | + () => { |
| 104 | + const q = toValue(query) |
| 105 | + if (!q.trim()) { |
| 106 | + return Promise.resolve({ objects: [], total: 0, time: new Date().toISOString() } as NpmSearchResponse) |
| 107 | + } |
| 108 | + return registry.searchPackages(q, toValue(options)) |
| 109 | + }, |
| 110 | + { watch: [() => toValue(query), () => toValue(options)] }, |
| 111 | + ) |
| 112 | +} |
0 commit comments