-
-
Notifications
You must be signed in to change notification settings - Fork 449
Expand file tree
/
Copy pathreadme-loaders.ts
More file actions
154 lines (131 loc) · 4.28 KB
/
readme-loaders.ts
File metadata and controls
154 lines (131 loc) · 4.28 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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import * as v from 'valibot'
import { PackageRouteParamsSchema } from '#shared/schemas/package'
import {
CACHE_MAX_AGE_ONE_HOUR,
NPM_MISSING_README_SENTINEL,
NPM_README_TRUNCATION_THRESHOLD,
} from '#shared/utils/constants'
/** Standard README filenames to try when fetching from jsdelivr (case-sensitive CDN) */
const standardReadmeFilenames = [
'README.md',
'readme.md',
'Readme.md',
'README',
'readme',
'README.markdown',
'readme.markdown',
]
/** Matches standard README filenames (case-insensitive, for checking registry metadata) */
const standardReadmePattern = /^readme(?:\.md|\.markdown)?$/i
const JSDELIVR_README_FETCH_BATCH_SIZE = 3
export function isStandardReadme(filename: string | undefined): boolean {
return !!filename && standardReadmePattern.test(filename)
}
function buildReadmeFetchCandidates(readmeFilename: string | undefined): string[] {
return readmeFilename
? standardReadmeFilenames.filter(name => name !== readmeFilename)
: standardReadmeFilenames
}
/**
* Fetch README from jsdelivr CDN for a specific package version.
* Falls back through candidate README filenames in small parallel batches.
*/
export async function fetchReadmeFromJsdelivr(
packageName: string,
readmeFilenames: string[],
version?: string,
): Promise<string | null> {
const versionSuffix = version ? `@${version}` : ''
for (let index = 0; index < readmeFilenames.length; index += JSDELIVR_README_FETCH_BATCH_SIZE) {
const batch = readmeFilenames.slice(index, index + JSDELIVR_README_FETCH_BATCH_SIZE)
const responses = await Promise.all(
batch.map(async filename => {
try {
const url = `https://cdn.jsdelivr.net/npm/${packageName}${versionSuffix}/${filename}`
const response = await fetch(url)
if (!response.ok) {
return null
}
return response
} catch {
return null
}
}),
)
for (const response of responses) {
const text = await response?.text()
if (text?.trim()) {
return text
}
}
}
return null
}
export const resolvePackageReadmeSource = defineCachedFunction(
async (packagePath: string) => {
const pkgParamSegments = packagePath.split('/')
const { rawPackageName, rawVersion } = parsePackageParams(pkgParamSegments)
const { packageName, version } = v.parse(PackageRouteParamsSchema, {
packageName: rawPackageName,
version: rawVersion,
})
const packageData = await fetchNpmPackage(packageName)
let readmeContent: string | undefined
let readmeFilename: string | undefined
if (version) {
const versionData = packageData.versions[version]
if (versionData) {
readmeContent = versionData.readme
readmeFilename = versionData.readmeFilename
}
} else {
readmeContent = packageData.readme
readmeFilename = packageData.readmeFilename
}
const hasValidNpmReadme = readmeContent && readmeContent !== NPM_MISSING_README_SENTINEL
if (
!hasValidNpmReadme ||
!isStandardReadme(readmeFilename) ||
readmeContent!.length >= NPM_README_TRUNCATION_THRESHOLD
) {
const resolvedVersion = version ?? packageData['dist-tags']?.latest
// try fetching the given readme file first
let jsdelivrReadme =
readmeFilename &&
(await fetchReadmeFromJsdelivr(packageName, [readmeFilename], resolvedVersion))
// if it's unsucessful, fetch all known readme filenames
if (!jsdelivrReadme) {
const readmeCandidates = buildReadmeFetchCandidates(readmeFilename)
jsdelivrReadme = await fetchReadmeFromJsdelivr(
packageName,
readmeCandidates,
resolvedVersion,
)
}
// if we found sometihng, use it
if (jsdelivrReadme) {
readmeContent = jsdelivrReadme
}
}
if (!readmeContent || readmeContent === NPM_MISSING_README_SENTINEL) {
return {
packageName,
version,
markdown: undefined,
repoInfo: undefined,
}
}
const repoInfo = parseRepositoryInfo(packageData.repository)
return {
packageName,
version,
markdown: readmeContent,
repoInfo,
}
},
{
maxAge: CACHE_MAX_AGE_ONE_HOUR,
swr: true,
getKey: (packagePath: string) => packagePath,
},
)