Skip to content

Commit 91a16ee

Browse files
committed
refactor: use /raw/ server route instead of middleware for markdown
Address review feedback from @atinux: - Replace middleware with server route at /raw/[...slug].md - Update vercel.json rewrites to point to /raw/:path.md - Add curl user-agent rewrite support for CLI tools - Enable CDN-level caching for markdown responses Also fix maintainer URLs to use npmx.dev instead of npmjs.com
1 parent c975020 commit 91a16ee

File tree

4 files changed

+45
-60
lines changed

4 files changed

+45
-60
lines changed

nuxt.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export default defineNuxtConfig({
7777
'/': { prerender: true },
7878
'/opensearch.xml': { isr: true },
7979
'/**': { isr: 60 },
80-
'/*.md': { isr: 60 },
80+
'/raw/**': { isr: 60 },
8181
'/package/**': { isr: 60 },
8282
'/search': { isr: false, cache: false },
8383
// infinite cache (versioned - doesn't change)
Lines changed: 36 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { generatePackageMarkdown } from '../utils/markdown'
1+
import { generatePackageMarkdown } from '../../utils/markdown'
22
import * as v from 'valibot'
33
import { PackageRouteParamsSchema } from '#shared/schemas/package'
44
import {
@@ -92,11 +92,11 @@ function isStandardReadme(filename: string | undefined): boolean {
9292
return !!filename && standardReadmePattern.test(filename)
9393
}
9494

95-
function parsePackageParamsFromPath(path: string): {
95+
function parsePackageParamsFromSlug(slug: string): {
9696
rawPackageName: string
9797
rawVersion: string | undefined
9898
} {
99-
const segments = path.slice(1).split('/').filter(Boolean)
99+
const segments = slug.split('/').filter(Boolean)
100100

101101
if (segments.length === 0) {
102102
return { rawPackageName: '', rawVersion: undefined }
@@ -127,8 +127,22 @@ function parsePackageParamsFromPath(path: string): {
127127
}
128128
}
129129

130-
async function handleMarkdownRequest(packagePath: string): Promise<string> {
131-
const { rawPackageName, rawVersion } = parsePackageParamsFromPath(packagePath)
130+
export default defineEventHandler(async event => {
131+
// Get the slug parameter - Nitro captures it as "slug.md" due to the route pattern
132+
const params = getRouterParams(event)
133+
const slugParam = params['slug.md'] || params.slug
134+
135+
if (!slugParam) {
136+
throw createError({
137+
statusCode: 404,
138+
statusMessage: 'Package not found',
139+
})
140+
}
141+
142+
// Remove .md suffix if present (it will be there from the route)
143+
const slug = slugParam.endsWith('.md') ? slugParam.slice(0, -3) : slugParam
144+
145+
const { rawPackageName, rawVersion } = parsePackageParamsFromSlug(slug)
132146

133147
if (!rawPackageName) {
134148
throw createError({
@@ -142,7 +156,15 @@ async function handleMarkdownRequest(packagePath: string): Promise<string> {
142156
version: rawVersion,
143157
})
144158

145-
const packageData = await fetchNpmPackage(packageName)
159+
let packageData
160+
try {
161+
packageData = await fetchNpmPackage(packageName)
162+
} catch {
163+
throw createError({
164+
statusCode: 502,
165+
statusMessage: ERROR_NPM_FETCH_FAILED,
166+
})
167+
}
146168

147169
let targetVersion = version
148170
if (!targetVersion) {
@@ -193,63 +215,21 @@ async function handleMarkdownRequest(packagePath: string): Promise<string> {
193215

194216
const repoInfo = parseRepositoryInfo(packageData.repository)
195217

196-
return generatePackageMarkdown({
218+
const markdown = generatePackageMarkdown({
197219
pkg: packageData,
198220
version: versionData,
199221
readme: readmeContent && readmeContent !== NPM_MISSING_README_SENTINEL ? readmeContent : null,
200222
weeklyDownloads: weeklyDownloadsData?.downloads,
201223
dailyDownloads: dailyDownloads ?? undefined,
202224
repoInfo,
203225
})
204-
}
205226

206-
/** Handle .md suffix and Accept: text/markdown header requests */
207-
export default defineEventHandler(async event => {
208-
const url = getRequestURL(event)
209-
const path = url.pathname
227+
setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8')
228+
setHeader(
229+
event,
230+
'Cache-Control',
231+
`public, max-age=${CACHE_MAX_AGE_ONE_HOUR}, stale-while-revalidate`,
232+
)
210233

211-
if (
212-
path.startsWith('/api/') ||
213-
path.startsWith('/_') ||
214-
path.startsWith('/__') ||
215-
path === '/search' ||
216-
path.startsWith('/search') ||
217-
path.startsWith('/code/') ||
218-
path === '/' ||
219-
path === '/.md'
220-
) {
221-
return
222-
}
223-
224-
const isMarkdownPath = path.endsWith('.md') && path.length > 3
225-
const acceptHeader = getHeader(event, 'accept') ?? ''
226-
const wantsMarkdown = acceptHeader.includes('text/markdown')
227-
228-
if (!isMarkdownPath && !wantsMarkdown) {
229-
return
230-
}
231-
232-
const packagePath = isMarkdownPath ? path.slice(0, -3) : path
233-
234-
try {
235-
const markdown = await handleMarkdownRequest(packagePath)
236-
237-
setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8')
238-
setHeader(
239-
event,
240-
'Cache-Control',
241-
`public, max-age=${CACHE_MAX_AGE_ONE_HOUR}, stale-while-revalidate`,
242-
)
243-
244-
return markdown
245-
} catch (error: unknown) {
246-
if (error && typeof error === 'object' && 'statusCode' in error) {
247-
throw error
248-
}
249-
250-
throw createError({
251-
statusCode: 502,
252-
statusMessage: ERROR_NPM_FETCH_FAILED,
253-
})
254-
}
234+
return markdown
255235
})

server/utils/markdown.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ export function generatePackageMarkdown(options: PackageMarkdownOptions): string
251251
const username = (maintainer as { username?: string }).username
252252
const name = maintainer.name || username || 'Unknown'
253253
if (username) {
254-
lines.push(`- [${name}](https://www.npmjs.com/~${username})`)
254+
lines.push(`- [${name}](https://npmx.dev/~${username})`)
255255
} else {
256256
lines.push(`- ${name}`)
257257
}

vercel.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22
"$schema": "https://openapi.vercel.sh/vercel.json",
33
"rewrites": [
44
{
5-
"source": "/:path((?!api|_nuxt|_v|__nuxt|search|code|\\.md$).*)",
5+
"source": "/:path((?!api|_nuxt|_v|__nuxt|search|code|raw/).*)",
66
"has": [{ "type": "header", "key": "accept", "value": "(.*?)text/markdown(.*)" }],
7-
"destination": "/:path.md"
7+
"destination": "/raw/:path.md"
8+
},
9+
{
10+
"source": "/:path((?!api|_nuxt|_v|__nuxt|search|code|raw/).*)",
11+
"has": [{ "type": "header", "key": "user-agent", "value": "curl/.*" }],
12+
"destination": "/raw/:path.md"
813
}
914
],
1015
"redirects": [

0 commit comments

Comments
 (0)