Skip to content

Commit c7dcdac

Browse files
committed
fix: add implementation
1 parent 827e318 commit c7dcdac

5 files changed

Lines changed: 44 additions & 63 deletions

File tree

app/composables/useNpmRegistry.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,6 @@ async function fetchBulkDownloads(
9292
return downloads
9393
}
9494

95-
/**
96-
* Encode a package name for use in npm registry URLs.
97-
* Handles scoped packages (e.g., @scope/name -> @scope%2Fname).
98-
*/
99-
export function encodePackageName(name: string): string {
100-
if (name.startsWith('@')) {
101-
return `@${encodeURIComponent(name.slice(1))}`
102-
}
103-
return encodeURIComponent(name)
104-
}
105-
10695
/** Number of recent versions to include in initial payload */
10796
const RECENT_VERSIONS_COUNT = 5
10897

app/composables/usePackageComparison.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { FacetValue, ComparisonFacet, ComparisonPackage } from '#shared/types'
2+
import { encodePackageName } from '#shared/utils/npm'
23
import type { PackageAnalysisResponse } from './usePackageAnalysis'
34

45
export interface PackageComparisonData {
@@ -222,13 +223,6 @@ export function usePackageComparison(packageNames: MaybeRefOrGetter<string[]>) {
222223
}
223224
}
224225

225-
function encodePackageName(name: string): string {
226-
if (name.startsWith('@')) {
227-
return `@${encodeURIComponent(name.slice(1))}`
228-
}
229-
return encodeURIComponent(name)
230-
}
231-
232226
function computeFacetValue(facet: ComparisonFacet, data: PackageComparisonData): FacetValue | null {
233227
switch (facet) {
234228
case 'downloads':

app/pages/docs/[...path].vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import { setResponseHeader } from 'h3'
33
import type { DocsResponse } from '#shared/types'
4-
import { assertValidPackageName } from '#shared/utils/npm'
4+
import { assertValidPackageName, fetchLatestVersion } from '#shared/utils/npm'
55
66
definePageMeta({
77
name: 'docs',
@@ -39,7 +39,7 @@ const { data: pkg } = usePackage(packageName)
3939
4040
const latestVersion = computed(() => pkg.value?.['dist-tags']?.latest ?? null)
4141
42-
if (import.meta.server && !requestedVersion.value) {
42+
if (import.meta.server && !requestedVersion.value && packageName.value) {
4343
const app = useNuxtApp()
4444
const version = await fetchLatestVersion(packageName.value)
4545
if (version) {

server/utils/npm.ts

Lines changed: 9 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
1-
import type { FastNpmMetaResponse, Packument } from '#shared/types'
1+
import type { Packument } from '#shared/types'
2+
import { encodePackageName, fetchLatestVersion as _fetchLatestVersion } from '#shared/utils/npm'
23
import { maxSatisfying, prerelease } from 'semver'
34

45
const NPM_REGISTRY = 'https://registry.npmjs.org'
5-
const FAST_NPM_META_API = 'https://npm.antfu.dev'
6-
7-
function encodePackageName(name: string): string {
8-
if (name.startsWith('@')) {
9-
return `@${encodeURIComponent(name.slice(1))}`
10-
}
11-
return encodeURIComponent(name)
12-
}
136

147
export const fetchNpmPackage = defineCachedFunction(
158
async (name: string): Promise<Packument> => {
@@ -24,31 +17,6 @@ export const fetchNpmPackage = defineCachedFunction(
2417
},
2518
)
2619

27-
/**
28-
* Fetch lightweight package metadata from fast-npm-meta API.
29-
* Much smaller payload than full packument - ideal for just getting latest version.
30-
*
31-
* @param name Package name
32-
* @param specifier Optional version specifier (tag like "alpha", or range like "^2.1.0")
33-
* @returns Resolved version info
34-
* @see https://github.com/antfu/fast-npm-meta
35-
*/
36-
export const fetchFastNpmMeta = defineCachedFunction(
37-
async (name: string, specifier?: string): Promise<FastNpmMetaResponse> => {
38-
const encodedName = encodePackageName(name)
39-
const url = specifier
40-
? `${FAST_NPM_META_API}/${encodedName}@${encodeURIComponent(specifier)}`
41-
: `${FAST_NPM_META_API}/${encodedName}`
42-
return await $fetch<FastNpmMetaResponse>(url)
43-
},
44-
{
45-
maxAge: 60 * 5,
46-
swr: true,
47-
name: 'fast-npm-meta',
48-
getKey: (name: string, specifier?: string) => (specifier ? `${name}@${specifier}` : name),
49-
},
50-
)
51-
5220
/**
5321
* Get the latest version of a package using fast-npm-meta API.
5422
* Falls back to full packument if fast-npm-meta fails.
@@ -57,17 +25,15 @@ export const fetchFastNpmMeta = defineCachedFunction(
5725
* @returns Latest version string or null if not found
5826
*/
5927
export async function fetchLatestVersion(name: string): Promise<string | null> {
28+
const version = await _fetchLatestVersion(name)
29+
if (version) return version
30+
31+
// Fallback to full packument (also cached)
6032
try {
61-
const meta = await fetchFastNpmMeta(name)
62-
return meta.version
33+
const packument = await fetchNpmPackage(name)
34+
return packument['dist-tags']?.latest ?? null
6335
} catch {
64-
// Fallback to full packument
65-
try {
66-
const packument = await fetchNpmPackage(name)
67-
return packument['dist-tags']?.latest ?? null
68-
} catch {
69-
return null
70-
}
36+
return null
7137
}
7238
}
7339

shared/utils/npm.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,38 @@
1+
import type { FastNpmMetaResponse } from '#shared/types'
12
import { createError } from 'h3'
23
import validatePackageName from 'validate-npm-package-name'
34

5+
export const FAST_NPM_META_API = 'https://npm.antfu.dev'
6+
7+
/**
8+
* Encode package name for URL usage.
9+
* Scoped packages need special handling (@scope/name → @scope%2Fname)
10+
*/
11+
export function encodePackageName(name: string): string {
12+
if (name.startsWith('@')) {
13+
return `@${encodeURIComponent(name.slice(1))}`
14+
}
15+
return encodeURIComponent(name)
16+
}
17+
18+
/**
19+
* Fetch the latest version of a package using fast-npm-meta API.
20+
* This is a lightweight alternative to fetching the full packument.
21+
*
22+
* @param name Package name
23+
* @returns Latest version string or null if not found
24+
* @see https://github.com/antfu/fast-npm-meta
25+
*/
26+
export async function fetchLatestVersion(name: string): Promise<string | null> {
27+
try {
28+
const encodedName = encodePackageName(name)
29+
const meta = await $fetch<FastNpmMetaResponse>(`${FAST_NPM_META_API}/${encodedName}`)
30+
return meta.version
31+
} catch {
32+
return null
33+
}
34+
}
35+
436
/**
537
* Validate an npm package name and throw an HTTP error if invalid.
638
* Uses validate-npm-package-name to check against npm naming rules.

0 commit comments

Comments
 (0)