forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1/**
2 * Redirect legacy/shorthand URLs to canonical paths.
3 *
4 * Handled here:
5 * - /@org/pkg or /pkg → /package/@org/pkg or /package/pkg
6 * - /@org/pkg/v/ver or /pkg@ver → /package/@org/pkg/v/ver or /package/pkg/v/ver
7 * - /@org → /org/org
8 *
9 * Handled via route aliases (not here):
10 * - /package/code/* → /package-code/*
11 * - /code/* → /package-code/*
12 * - /package/docs/* → /package-docs/*
13 * - /docs/* → /package-docs/*
14 */
15const pages = [
16 '/oauth-client-metadata.json',
17 '/200.html',
18 '/about',
19 '/accessibility',
20 '/compare',
21 '/org',
22 '/package',
23 '/package-code',
24 '/package-docs',
25 '/privacy',
26 '/search',
27 '/settings',
28 '/recharging',
29]
30
31const cacheControl = 's-maxage=3600, stale-while-revalidate=36000'
32
33export default defineEventHandler(async event => {
34 const routeRules = getRouteRules(event)
35 if (Object.keys(routeRules).length > 1) {
36 return
37 }
38
39 const [path = '/', query] = event.path.split('?')
40
41 // username
42 if (path.startsWith('/~') || path.startsWith('/_')) {
43 return
44 }
45
46 if (pages.some(page => path === page || path.startsWith(page + '/'))) {
47 return
48 }
49
50 // /@org/pkg or /pkg → /package/org/pkg or /package/pkg
51 let pkgMatch = path.match(/^\/(?:(?<org>@[^/]+)\/)?(?<name>[^/@]+)$/)
52 if (pkgMatch?.groups) {
53 const args = [pkgMatch.groups.org, pkgMatch.groups.name].filter(Boolean).join('/')
54 setHeader(event, 'cache-control', cacheControl)
55 return sendRedirect(event, `/package/${args}` + (query ? '?' + query : ''), 301)
56 }
57
58 // /@org/pkg/v/version or /@org/pkg@version → /package/org/pkg/v/version
59 // /pkg/v/version or /pkg@version → /package/pkg/v/version
60 const pkgVersionMatch =
61 path.match(/^\/(?:(?<org>@[^/]+)\/)?(?<name>[^/@]+)\/v\/(?<version>[^/]+)$/) ||
62 path.match(/^\/(?:(?<org>@[^/]+)\/)?(?<name>[^/@]+)@(?<version>[^/]+)$/)
63
64 if (pkgVersionMatch?.groups) {
65 const args = [pkgVersionMatch.groups.org, pkgVersionMatch.groups.name].filter(Boolean).join('/')
66 setHeader(event, 'cache-control', cacheControl)
67 return sendRedirect(
68 event,
69 `/package/${args}/v/${pkgVersionMatch.groups.version}` + (query ? '?' + query : ''),
70 301,
71 )
72 }
73
74 // /@org → /org/org
75 const orgMatch = path.match(/^\/@(?<org>[^/]+)$/)
76 if (orgMatch?.groups) {
77 setHeader(event, 'cache-control', cacheControl)
78 return sendRedirect(event, `/org/${orgMatch.groups.org}` + (query ? '?' + query : ''), 301)
79 }
80})