Skip to content

Commit 4bf7317

Browse files
mikouajidanielroe
andauthored
feat: new routing schema (#674)
Co-authored-by: Daniel Roe <daniel@roe.dev>
1 parent e46df53 commit 4bf7317

File tree

23 files changed

+150
-134
lines changed

23 files changed

+150
-134
lines changed

.lighthouserc.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ module.exports = {
2929
url: [
3030
'http://localhost:3000/',
3131
'http://localhost:3000/search?q=nuxt',
32-
'http://localhost:3000/nuxt',
32+
'http://localhost:3000/package/nuxt',
3333
],
3434
numberOfRuns: 1,
3535
chromePath: findChrome(),

app/components/Header/PackagesDropdown.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ function handleKeydown(event: KeyboardEvent) {
9494
<ul v-else-if="packages.length > 0" class="py-1 max-h-80 overflow-y-auto">
9595
<li v-for="pkg in packages" :key="pkg">
9696
<NuxtLink
97-
:to="`/${pkg}`"
97+
:to="`/package/${pkg}`"
9898
class="block px-3 py-2 font-mono text-sm text-fg hover:bg-bg-subtle transition-colors truncate"
9999
>
100100
{{ pkg }}

app/components/Package/MetricsBadges.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const typesTooltip = computed(() => {
4545
const typesHref = computed(() => {
4646
if (!analysis.value) return null
4747
if (analysis.value.types?.kind === '@types') {
48-
return `/${analysis.value.types.packageName}`
48+
return `/package/${analysis.value.types.packageName}`
4949
}
5050
return null
5151
})

app/components/Package/SkillsModal.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const props = defineProps<{
88
}>()
99
1010
function getSkillSourceUrl(skill: SkillListItem): string {
11-
const base = `/code/${props.packageName}`
11+
const base = `/package-code/${props.packageName}`
1212
const versionPath = props.version ? `/v/${props.version}` : ''
1313
return `${base}${versionPath}/skills/${skill.dirName}/SKILL.md`
1414
}
@@ -101,7 +101,7 @@ function getWarningTooltip(skill: SkillListItem): string | undefined {
101101
</template>
102102
</i18n-t>
103103
<a
104-
href="/skills-npm"
104+
href="/package/skills-npm"
105105
class="inline-flex items-center gap-1 text-xs text-fg-subtle hover:text-fg transition-colors shrink-0"
106106
>
107107
{{ $t('package.skills.learn_more') }}

app/components/Terminal/Install.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ const copyCreateCommand = () => copyCreate(getFullCreateCommand())
149149
></code
150150
>
151151
<NuxtLink
152-
:to="`/${typesPackageName}`"
152+
:to="`/package/${typesPackageName}`"
153153
class="text-fg-subtle hover:text-fg-muted text-xs transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 rounded"
154154
:title="$t('package.get_started.view_types', { package: typesPackageName })"
155155
>
@@ -228,7 +228,7 @@ const copyCreateCommand = () => copyCreate(getFullCreateCommand())
228228
}}</span>
229229
</button>
230230
<NuxtLink
231-
:to="`/${createPackageInfo.packageName}`"
231+
:to="`/package/${createPackageInfo.packageName}`"
232232
class="text-fg-subtle hover:text-fg-muted text-xs transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 rounded"
233233
:title="`View ${createPackageInfo.packageName}`"
234234
>

app/components/VersionSelector.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ watch(
620620
<!-- Link to package page for full version list -->
621621
<div class="border-t border-border mt-1 pt-1 px-3 py-2">
622622
<NuxtLink
623-
:to="`/${packageName}`"
623+
:to="`/package/${packageName}`"
624624
class="text-xs text-fg-subtle hover:text-fg transition-[color] focus-visible:outline-none focus-visible:text-fg"
625625
@click="isOpen = false"
626626
>

app/components/compare/PackageSelector.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function handleBlur() {
6464
class="inline-flex items-center gap-2 px-3 py-1.5 bg-bg-subtle border border-border rounded-md"
6565
>
6666
<NuxtLink
67-
:to="`/${pkg}`"
67+
:to="`/package/${pkg}`"
6868
class="font-mono text-sm text-fg hover:text-accent transition-colors"
6969
>
7070
{{ pkg }}
Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,49 @@
11
/**
22
* Redirect legacy URLs to canonical paths (client-side only)
33
*
4-
* - /package/* → /*
5-
* - /package/code/* → /code/*
4+
* - /package/code/* → /package-code/*
5+
* - /code/* → /package-code/*
6+
* - /package/docs/* → /package-docs/*
7+
* - /docs/* → /package-docs/*
68
* - /org/* → /@*
9+
* - /* → /package/* (Unless its an existing page)
710
*/
811
export default defineNuxtRouteMiddleware(to => {
912
// Only redirect on client-side to avoid breaking crawlers mid-transition
1013
if (import.meta.server) return
1114

1215
const path = to.path
1316

14-
// /package/code/* → /code/*
17+
// /package/code/* → /package-code/*
1518
if (path.startsWith('/package/code/')) {
16-
return navigateTo(path.replace('/package/code/', '/code/'), { replace: true })
19+
return navigateTo(path.replace('/package/code/', '/package-code/'), { replace: true })
20+
}
21+
// /code/* → /package-code/*
22+
if (path.startsWith('/code/')) {
23+
return navigateTo(path.replace('/code/', '/package-code/'), { replace: true })
1724
}
1825

19-
// /package/* → /*
20-
if (path.startsWith('/package/')) {
21-
return navigateTo(path.replace('/package/', '/'), { replace: true })
26+
// /package/docs/* → /package-docs/*
27+
if (path.startsWith('/package/docs/')) {
28+
return navigateTo(path.replace('/package/docs/', '/package-docs/'), { replace: true })
29+
}
30+
// /docs/* → /package-docs/*
31+
if (path.startsWith('/docs/')) {
32+
return navigateTo(path.replace('/docs/', '/package-docs/'), { replace: true })
2233
}
2334

2435
// /org/* → /@*
2536
if (path.startsWith('/org/')) {
2637
return navigateTo(path.replace('/org/', '/@'), { replace: true })
2738
}
39+
40+
// Keep this one last as it will catch everything
41+
// /* → /package/* (Unless its an existing page)
42+
if (path.startsWith('/') && !path.startsWith('/package/')) {
43+
const router = useRouter()
44+
const resolved = router.resolve(path)
45+
if (resolved?.matched?.length === 1 && resolved.matched[0]?.path === '/:package(.*)*') {
46+
return navigateTo(`/package${path}`, { replace: true })
47+
}
48+
}
2849
})
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import { formatBytes } from '~/utils/formatters'
88
99
definePageMeta({
1010
name: 'code',
11-
alias: ['/package/code/:path(.*)*'],
11+
path: '/package-code/:path+',
12+
alias: ['/package/code/:path+', '/code/:path+'],
1213
})
1314
1415
const route = useRoute('code')
@@ -19,7 +20,7 @@ const route = useRoute('code')
1920
// /code/nuxt/v/4.2.0/src/index.ts → packageName: "nuxt", version: "4.2.0", filePath: "src/index.ts"
2021
// /code/@nuxt/kit/v/1.0.0 → packageName: "@nuxt/kit", version: "1.0.0", filePath: null
2122
const parsedRoute = computed(() => {
22-
const segments = route.params.path || []
23+
const segments = route.params.path
2324
2425
// Find the /v/ separator for version
2526
const vIndex = segments.indexOf('v')
@@ -50,7 +51,7 @@ const { data: pkg } = usePackage(packageName)
5051
5152
// URL pattern for version selector - includes file path if present
5253
const versionUrlPattern = computed(() => {
53-
const base = `/code/${packageName.value}/v/{version}`
54+
const base = `/package-code/${packageName.value}/v/{version}`
5455
return filePath.value ? `${base}/${filePath.value}` : base
5556
})
5657
@@ -193,7 +194,7 @@ const breadcrumbs = computed(() => {
193194
194195
// Navigation helper - build URL for a path
195196
function getCodeUrl(path?: string): string {
196-
const base = `/code/${packageName.value}/v/${version.value}`
197+
const base = `/package-code/${packageName.value}/v/${version.value}`
197198
return path ? `${base}/${path}` : base
198199
}
199200
@@ -248,7 +249,7 @@ function copyPermalinkUrl() {
248249
249250
// Canonical URL for this code page
250251
const canonicalUrl = computed(() => {
251-
let url = `https://npmx.dev/code/${packageName.value}/v/${version.value}`
252+
let url = `https://npmx.dev/package-code/${packageName.value}/v/${version.value}`
252253
if (filePath.value) {
253254
url += `/${filePath.value}`
254255
}
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import { assertValidPackageName, fetchLatestVersion } from '#shared/utils/npm'
55
66
definePageMeta({
77
name: 'docs',
8+
path: '/package-docs/:path+',
9+
alias: ['/package/docs/:path+', '/docs/:path+'],
810
})
911
1012
const route = useRoute('docs')
1113
const router = useRouter()
1214
1315
const parsedRoute = computed(() => {
14-
const segments = route.params.path?.filter(Boolean) || []
16+
const segments = route.params.path?.filter(Boolean)
1517
const vIndex = segments.indexOf('v')
1618
1719
if (vIndex === -1 || vIndex >= segments.length - 1) {
@@ -45,7 +47,7 @@ if (import.meta.server && !requestedVersion.value && packageName.value) {
4547
if (version) {
4648
setResponseHeader(useRequestEvent()!, 'Cache-Control', 'no-cache')
4749
app.runWithContext(() =>
48-
navigateTo('/docs/' + packageName.value + '/v/' + version, { redirectCode: 302 }),
50+
navigateTo('/package-docs/' + packageName.value + '/v/' + version, { redirectCode: 302 }),
4951
)
5052
}
5153
}
@@ -54,7 +56,7 @@ watch(
5456
[requestedVersion, latestVersion, packageName],
5557
([version, latest, name]) => {
5658
if (!version && latest && name) {
57-
router.replace(`/docs/${name}/v/${latest}`)
59+
router.replace(`/package-docs/${name}/v/${latest}`)
5860
}
5961
},
6062
{ immediate: true },
@@ -120,7 +122,7 @@ const showEmptyState = computed(() => docsData.value?.status !== 'ok')
120122
<div class="flex items-center gap-3 min-w-0">
121123
<NuxtLink
122124
v-if="packageName"
123-
:to="`/${packageName}`"
125+
:to="{ name: 'package', params: { package: [packageName] } }"
124126
class="font-mono text-lg sm:text-xl font-semibold text-fg hover:text-fg-muted transition-colors truncate"
125127
>
126128
{{ packageName }}
@@ -131,7 +133,7 @@ const showEmptyState = computed(() => docsData.value?.status !== 'ok')
131133
:current-version="resolvedVersion"
132134
:versions="pkg.versions"
133135
:dist-tags="pkg['dist-tags']"
134-
:url-pattern="`/docs/${packageName}/v/{version}`"
136+
:url-pattern="`/package-docs/${packageName}/v/{version}`"
135137
/>
136138
<span v-else-if="resolvedVersion" class="text-fg-subtle font-mono text-sm shrink-0">
137139
{{ resolvedVersion }}
@@ -179,7 +181,7 @@ const showEmptyState = computed(() => docsData.value?.status !== 'ok')
179181
<div class="flex gap-4 mt-4">
180182
<NuxtLink
181183
v-if="packageName"
182-
:to="`/${packageName}`"
184+
:to="{ name: 'package', params: { package: [packageName] } }"
183185
class="link-subtle font-mono text-sm"
184186
>
185187
View package

0 commit comments

Comments
 (0)