Skip to content

Commit ff5da1a

Browse files
fix: prefer classic readme if npm picks up a different filename (#130)
1 parent c79a304 commit ff5da1a

1 file changed

Lines changed: 40 additions & 14 deletions

File tree

server/api/registry/readme/[...pkg].get.ts

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,32 @@ import {
66
ERROR_NPM_FETCH_FAILED,
77
} from '#shared/utils/constants'
88

9+
/** Standard README filenames to try when fetching from jsdelivr (case-sensitive CDN) */
10+
const standardReadmeFilenames = [
11+
'README.md',
12+
'readme.md',
13+
'Readme.md',
14+
'README',
15+
'readme',
16+
'README.markdown',
17+
'readme.markdown',
18+
]
19+
20+
/** Matches standard README filenames (case-insensitive, for checking registry metadata) */
21+
const standardReadmePattern = /^readme(\.md|\.markdown)?$/i
22+
923
/**
1024
* Fetch README from jsdelivr CDN for a specific package version.
1125
* Falls back through common README filenames.
1226
*/
1327
async function fetchReadmeFromJsdelivr(
1428
packageName: string,
29+
readmeFilenames: string[],
1530
version?: string,
1631
): Promise<string | null> {
17-
const filenames = [
18-
'README.md',
19-
'readme.md',
20-
'Readme.md',
21-
'README',
22-
'readme',
23-
'README.markdown',
24-
'readme.markdown',
25-
]
2632
const versionSuffix = version ? `@${version}` : ''
2733

28-
for (const filename of filenames) {
34+
for (const filename of readmeFilenames) {
2935
try {
3036
const url = `https://cdn.jsdelivr.net/npm/${packageName}${versionSuffix}/${filename}`
3137
const response = await fetch(url)
@@ -67,24 +73,40 @@ export default defineCachedEventHandler(
6773
const packageData = await fetchNpmPackage(packageName)
6874

6975
let readmeContent: string | undefined
76+
let readmeFilename: string | undefined
7077

7178
// If a specific version is requested, get README from that version
7279
if (version) {
7380
const versionData = packageData.versions[version]
7481
if (versionData) {
7582
readmeContent = versionData.readme
83+
readmeFilename = versionData.readmeFilename
7684
}
7785
} else {
7886
// Use the packument-level readme (from latest version)
7987
readmeContent = packageData.readme
88+
readmeFilename = packageData.readmeFilename
8089
}
8190

82-
// If no README in packument, try fetching from jsdelivr (package tarball)
83-
if (!readmeContent || readmeContent === NPM_MISSING_README_SENTINEL) {
84-
readmeContent = (await fetchReadmeFromJsdelivr(packageName, version)) ?? undefined
91+
const hasValidNpmReadme = readmeContent && readmeContent !== NPM_MISSING_README_SENTINEL
92+
93+
// If no README in packument, or if readmeFilename is non-standard (e.g., README.zh-TW.md),
94+
// try fetching a standard README from jsdelivr (package tarball).
95+
// Note: When readmeFilename is missing, we defensively fetch from jsdelivr to ensure
96+
// we get a standard English README if one exists.
97+
if (!hasValidNpmReadme || !isStandardReadme(readmeFilename)) {
98+
const jsdelivrReadme = await fetchReadmeFromJsdelivr(
99+
packageName,
100+
standardReadmeFilenames,
101+
version,
102+
)
103+
// Only replace npm content if jsdelivr returned something
104+
if (jsdelivrReadme) {
105+
readmeContent = jsdelivrReadme
106+
}
85107
}
86108

87-
if (!readmeContent) {
109+
if (!readmeContent || readmeContent === NPM_MISSING_README_SENTINEL) {
88110
return { html: '', playgroundLinks: [] }
89111
}
90112

@@ -108,3 +130,7 @@ export default defineCachedEventHandler(
108130
},
109131
},
110132
)
133+
134+
function isStandardReadme(filename: string | undefined): boolean {
135+
return !!filename && standardReadmePattern.test(filename)
136+
}

0 commit comments

Comments
 (0)