From b469918755fd81cef8683fa0d34cfcd59df4d8f3 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 27 Mar 2026 14:26:20 -0700 Subject: [PATCH 1/3] feat: add redirects for nuxt error documentation --- app/pages/docs/[...slug].vue | 2 +- content.config.ts | 3 ++- nuxt.config.ts | 1 + server/middleware/error-docs-redirect.ts | 17 +++++++++++++++++ server/routes/e/[code].get.ts | 24 ++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 server/middleware/error-docs-redirect.ts create mode 100644 server/routes/e/[code].get.ts diff --git a/app/pages/docs/[...slug].vue b/app/pages/docs/[...slug].vue index 9209835fd..651621a7e 100644 --- a/app/pages/docs/[...slug].vue +++ b/app/pages/docs/[...slug].vue @@ -79,7 +79,7 @@ watch(page, (page) => { }, { immediate: true }) // Get the -2 item of the breadcrumb -const currentSectionTitle = computed(() => headerLinks.value[0].children.find(link => path.value.includes(link.to))?.label || findPageBreadcrumb(navigation.value, path.value).slice(-1)[0].title) +const currentSectionTitle = computed(() => headerLinks.value[0].children.find(link => path.value.includes(link.to))?.label || findPageBreadcrumb(navigation.value, path.value).slice(-1)[0]?.title || page.value?.title) const breadcrumb = computed(() => { const links = mapContentNavigation(findPageBreadcrumb(navigation.value, path.value)).map(link => ({ diff --git a/content.config.ts b/content.config.ts index 2240c86d5..88d2e62e3 100644 --- a/content.config.ts +++ b/content.config.ts @@ -10,7 +10,8 @@ const docsV3Source = { const docsV4Source = { cwd: process.env.NUXT_V4_PATH ?? undefined, - repository: !process.env.NUXT_V4_PATH ? 'https://github.com/nuxt/nuxt/tree/4.x' : undefined, + // TODO: revert to 4.x branch before merging + repository: !process.env.NUXT_V4_PATH ? 'https://github.com/nuxt/nuxt/tree/feat/error-context' : undefined, include: 'docs/**/*', exclude: ['docs/**/*.json'], prefix: '/docs/4.x' diff --git a/nuxt.config.ts b/nuxt.config.ts index eda446ac7..43d4a3b6e 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -117,6 +117,7 @@ export default defineNuxtConfig({ '/docs/3.x/getting-started/introduction': { prerender: true }, '/docs/4.x/getting-started/introduction': { prerender: true }, '/docs/5.x/getting-started/introduction': { prerender: true }, + '/docs/4.x/errors': { prerender: true }, '/modules': { prerender: true }, '/modules/**': { isr: 60 * 60 }, '/changelog': { isr: 60 * 60 }, diff --git a/server/middleware/error-docs-redirect.ts b/server/middleware/error-docs-redirect.ts new file mode 100644 index 000000000..682ef6c91 --- /dev/null +++ b/server/middleware/error-docs-redirect.ts @@ -0,0 +1,17 @@ +/** + * Redirect unversioned error doc paths to the current default version. + * + * The Nuxt runtime links to https://nuxt.com/docs/errors/E1001 (unversioned). + * This middleware redirects those paths to the current default docs version + * (e.g., /docs/4.x/errors/E1001) so the content can be resolved. + */ +export default defineEventHandler((event) => { + const path = getRequestURL(event).pathname + + // Match /docs/errors/... but NOT /docs/3.x/errors/... or /docs/4.x/errors/... + const match = path.match(/^\/docs\/errors\/(.+)$/) + if (match) { + // TODO: update to /docs/5.x when Nuxt 5 is the default + return sendRedirect(event, `/docs/4.x/errors/${match[1]}`, 302) + } +}) diff --git a/server/routes/e/[code].get.ts b/server/routes/e/[code].get.ts new file mode 100644 index 000000000..08bf01ee2 --- /dev/null +++ b/server/routes/e/[code].get.ts @@ -0,0 +1,24 @@ +/** + * Short URL handler for Nuxt error codes. + * + * Redirects /e/NUXT_E1001 → /docs/4.x/errors/E1001 + * Redirects /e/NUXT_B1001 → /docs/4.x/errors/B1001 + * + * The error code format is NUXT_ followed by E or B and 1-4 digits. + * The NUXT_ prefix is stripped and the user is redirected to the + * current default docs version. + */ +export default defineEventHandler((event) => { + const code = getRouterParam(event, 'code') + + // Validate: must be NUXT_ followed by E or B and 1-4 digits + if (!code || !/^NUXT_[EB]\d{1,4}$/.test(code)) { + throw createError({ statusCode: 404, statusMessage: 'Not found' }) + } + + // Strip the NUXT_ prefix → E1001 or B1001 + const errorCode = code.replace('NUXT_', '') + + // TODO: update to /docs/5.x when Nuxt 5 is the default + return sendRedirect(event, `/docs/4.x/errors/${errorCode}`, 302) +}) From 4f467fcb11be216485a44ff086c1f8d4e90b3f4f Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 27 Mar 2026 14:31:32 -0700 Subject: [PATCH 2/3] fix: use flat branch name to avoid isomorphic-git ref parsing issue --- content.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content.config.ts b/content.config.ts index 88d2e62e3..677696ad8 100644 --- a/content.config.ts +++ b/content.config.ts @@ -11,7 +11,7 @@ const docsV3Source = { const docsV4Source = { cwd: process.env.NUXT_V4_PATH ?? undefined, // TODO: revert to 4.x branch before merging - repository: !process.env.NUXT_V4_PATH ? 'https://github.com/nuxt/nuxt/tree/feat/error-context' : undefined, + repository: !process.env.NUXT_V4_PATH ? 'https://github.com/nuxt/nuxt/tree/error-context' : undefined, include: 'docs/**/*', exclude: ['docs/**/*.json'], prefix: '/docs/4.x' From 397f8ba4264c25d996faf7b13f94e52c4f20a138 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 27 Mar 2026 15:09:05 -0700 Subject: [PATCH 3/3] fix: lowercase error codes in redirects to match content collection paths --- server/middleware/error-docs-redirect.ts | 8 +++++--- server/routes/e/[code].get.ts | 14 +++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/server/middleware/error-docs-redirect.ts b/server/middleware/error-docs-redirect.ts index 682ef6c91..8407b175f 100644 --- a/server/middleware/error-docs-redirect.ts +++ b/server/middleware/error-docs-redirect.ts @@ -3,15 +3,17 @@ * * The Nuxt runtime links to https://nuxt.com/docs/errors/E1001 (unversioned). * This middleware redirects those paths to the current default docs version - * (e.g., /docs/4.x/errors/E1001) so the content can be resolved. + * (e.g., /docs/4.x/errors/e1001) so the content can be resolved. + * + * The error code is lowercased to match the content collection path. */ export default defineEventHandler((event) => { const path = getRequestURL(event).pathname // Match /docs/errors/... but NOT /docs/3.x/errors/... or /docs/4.x/errors/... const match = path.match(/^\/docs\/errors\/(.+)$/) - if (match) { + if (match?.[1]) { // TODO: update to /docs/5.x when Nuxt 5 is the default - return sendRedirect(event, `/docs/4.x/errors/${match[1]}`, 302) + return sendRedirect(event, `/docs/4.x/errors/${match[1].toLowerCase()}`, 302) } }) diff --git a/server/routes/e/[code].get.ts b/server/routes/e/[code].get.ts index 08bf01ee2..baaace893 100644 --- a/server/routes/e/[code].get.ts +++ b/server/routes/e/[code].get.ts @@ -1,23 +1,23 @@ /** * Short URL handler for Nuxt error codes. * - * Redirects /e/NUXT_E1001 → /docs/4.x/errors/E1001 - * Redirects /e/NUXT_B1001 → /docs/4.x/errors/B1001 + * Redirects /e/NUXT_E1001 → /docs/4.x/errors/e1001 + * Redirects /e/NUXT_B1001 → /docs/4.x/errors/b1001 * * The error code format is NUXT_ followed by E or B and 1-4 digits. - * The NUXT_ prefix is stripped and the user is redirected to the - * current default docs version. + * The NUXT_ prefix is stripped, the code is lowercased, and the user + * is redirected to the current default docs version. */ export default defineEventHandler((event) => { const code = getRouterParam(event, 'code') // Validate: must be NUXT_ followed by E or B and 1-4 digits - if (!code || !/^NUXT_[EB]\d{1,4}$/.test(code)) { + if (!code || !/^NUXT_[EB]\d{1,4}$/i.test(code)) { throw createError({ statusCode: 404, statusMessage: 'Not found' }) } - // Strip the NUXT_ prefix → E1001 or B1001 - const errorCode = code.replace('NUXT_', '') + // Strip the NUXT_ prefix and lowercase → e1001 or b1001 + const errorCode = code.replace('NUXT_', '').toLowerCase() // TODO: update to /docs/5.x when Nuxt 5 is the default return sendRedirect(event, `/docs/4.x/errors/${errorCode}`, 302)