forked from npmx-dev/npmx.dev
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path[...pkg].get.ts
More file actions
89 lines (79 loc) · 2.64 KB
/
[...pkg].get.ts
File metadata and controls
89 lines (79 loc) · 2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import * as v from 'valibot'
import { PackageRouteParamsSchema } from '#shared/schemas/package'
import type { PackageAnalysis, ExtendedPackageJson } from '#shared/utils/package-analysis'
import {
analyzePackage,
getTypesPackageName,
hasBuiltInTypes,
} from '#shared/utils/package-analysis'
import {
NPM_REGISTRY,
CACHE_MAX_AGE_ONE_DAY,
ERROR_PACKAGE_ANALYSIS_FAILED,
} from '#shared/utils/constants'
export default defineCachedEventHandler(
async event => {
// Parse package name and optional version from path
// e.g., "vue" or "vue/v/3.4.0" or "@nuxt/kit" or "@nuxt/kit/v/1.0.0"
const pkgParamSegments = getRouterParam(event, 'pkg')?.split('/') ?? []
const { rawPackageName, rawVersion } = parsePackageParams(pkgParamSegments)
try {
const { packageName, version } = v.parse(PackageRouteParamsSchema, {
packageName: rawPackageName,
version: rawVersion,
})
// Fetch package data
const encodedName = encodePackageName(packageName)
const versionSuffix = version ? `/${version}` : '/latest'
const pkg = await $fetch<ExtendedPackageJson>(
`${NPM_REGISTRY}/${encodedName}${versionSuffix}`,
)
// Only check for @types package if the package doesn't ship its own types
let typesPackageExists = false
if (!hasBuiltInTypes(pkg)) {
const typesPkgName = getTypesPackageName(packageName)
typesPackageExists = await checkPackageExists(typesPkgName)
}
const analysis = analyzePackage(pkg, { typesPackageExists })
return {
package: packageName,
version: pkg.version ?? version ?? 'latest',
...analysis,
} satisfies PackageAnalysisResponse
} catch (error: unknown) {
handleApiError(error, {
statusCode: 502,
message: ERROR_PACKAGE_ANALYSIS_FAILED,
})
}
},
{
maxAge: CACHE_MAX_AGE_ONE_DAY, // 24 hours - analysis rarely changes
swr: true,
getKey: event => {
const pkg = getRouterParam(event, 'pkg') ?? ''
return `analysis:v1:${pkg.replace(/\/+$/, '').trim()}`
},
},
)
function encodePackageName(name: string): string {
if (name.startsWith('@')) {
return `@${encodeURIComponent(name.slice(1))}`
}
return encodeURIComponent(name)
}
async function checkPackageExists(packageName: string): Promise<boolean> {
try {
const encodedName = encodePackageName(packageName)
const response = await $fetch.raw(`${NPM_REGISTRY}/${encodedName}`, {
method: 'HEAD',
})
return response.status === 200
} catch {
return false
}
}
export interface PackageAnalysisResponse extends PackageAnalysis {
package: string
version: string
}