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
105 lines (92 loc) · 3.29 KB
/
[...pkg].get.ts
File metadata and controls
105 lines (92 loc) · 3.29 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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/**
* Fetch README from jsdelivr CDN for a specific package version.
* Falls back through common README filenames.
*/
async function fetchReadmeFromJsdelivr(
packageName: string,
version?: string,
): Promise<string | null> {
const filenames = ['README.md', 'readme.md', 'Readme.md', 'README', 'readme']
const versionSuffix = version ? `@${version}` : ''
for (const filename of filenames) {
try {
const url = `https://cdn.jsdelivr.net/npm/${packageName}${versionSuffix}/${filename}`
const response = await fetch(url)
if (response.ok) {
return await response.text()
}
} catch {
// Try next filename
}
}
return null
}
/**
* Returns rendered README HTML for a package.
*
* URL patterns:
* - /api/registry/readme/packageName - latest version
* - /api/registry/readme/packageName/v/1.2.3 - specific version
* - /api/registry/readme/@scope/packageName - scoped package, latest
* - /api/registry/readme/@scope/packageName/v/1.2.3 - scoped package, specific version
*/
export default defineCachedEventHandler(
async event => {
const segments = getRouterParam(event, 'pkg')?.split('/') ?? []
if (segments.length === 0) {
throw createError({ statusCode: 400, message: 'Package name is required' })
}
// Parse package name and optional version from URL segments
// Patterns: [pkg] or [pkg, 'v', version] or [@scope, pkg] or [@scope, pkg, 'v', version]
let packageName: string
let version: string | undefined
const vIndex = segments.indexOf('v')
if (vIndex !== -1 && vIndex < segments.length - 1) {
packageName = segments.slice(0, vIndex).join('/')
version = segments.slice(vIndex + 1).join('/')
} else {
packageName = segments.join('/')
}
if (!packageName) {
throw createError({ statusCode: 400, message: 'Package name is required' })
}
assertValidPackageName(packageName)
try {
const packageData = await fetchNpmPackage(packageName)
let readmeContent: string | undefined
// If a specific version is requested, get README from that version
if (version) {
const versionData = packageData.versions[version]
if (versionData) {
readmeContent = versionData.readme
}
} else {
// Use the packument-level readme (from latest version)
readmeContent = packageData.readme
}
// If no README in packument, try fetching from jsdelivr (package tarball)
if (!readmeContent || readmeContent === 'ERROR: No README data found!') {
readmeContent = (await fetchReadmeFromJsdelivr(packageName, version)) ?? undefined
}
if (!readmeContent) {
return { html: '', playgroundLinks: [] }
}
// Parse repository info for resolving relative URLs to GitHub
const repoInfo = parseRepositoryInfo(packageData.repository)
return await renderReadmeHtml(readmeContent, packageName, repoInfo)
} catch (error) {
if (error && typeof error === 'object' && 'statusCode' in error) {
throw error
}
throw createError({ statusCode: 502, message: 'Failed to fetch package from npm registry' })
}
},
{
maxAge: 60 * 60, // 1 hour
swr: true,
getKey: event => {
const pkg = getRouterParam(event, 'pkg') ?? ''
return `readme:${pkg}`
},
},
)