From a06d5247cad07e76b837b325f44e8de48fed0a33 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Thu, 28 Sep 2023 11:54:22 +0100 Subject: [PATCH] feat(nuxt): add `prerenderRoutes` ssr composable (#22863) --- docs/3.api/3.utils/prerender-routes.md | 20 +++++++++++++++++++ docs/3.api/4.advanced/2.kit.md | 2 +- packages/kit/src/nitro.ts | 2 +- .../nuxt/src/app/components/nuxt-island.ts | 4 ++-- packages/nuxt/src/app/composables/index.ts | 2 +- packages/nuxt/src/app/composables/ssr.ts | 9 ++++++++- packages/nuxt/src/imports/presets.ts | 1 + .../components/ServerOnlyComponent.server.vue | 4 +--- test/nuxt/composables.test.ts | 2 ++ 9 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 docs/3.api/3.utils/prerender-routes.md diff --git a/docs/3.api/3.utils/prerender-routes.md b/docs/3.api/3.utils/prerender-routes.md new file mode 100644 index 0000000000..f672364210 --- /dev/null +++ b/docs/3.api/3.utils/prerender-routes.md @@ -0,0 +1,20 @@ +--- +description: prerenderRoutes hints to Nitro to prerender an additional route. +--- + +# `prerenderRoutes` + +When prerendering, you can hint to Nitro to prerender additional paths, even if their URLs do not show up in the HTML of the generated page. + +`prerenderRoutes` can only be called within component setup functions, plugins, and route middleware. + +```js +const route = useRoute() + +prerenderRoutes('/') +prerenderRoutes(['/', '/about']) +``` + +::alert{icon=👉} +In the browser, or if called outside prerendering, `prerenderRoutes` will have no effect. +:: diff --git a/docs/3.api/4.advanced/2.kit.md b/docs/3.api/4.advanced/2.kit.md index 25d4cd3c3b..811b95fa8e 100644 --- a/docs/3.api/4.advanced/2.kit.md +++ b/docs/3.api/4.advanced/2.kit.md @@ -88,7 +88,7 @@ description: Nuxt Kit provides composable utilities to help interacting with Nux - `addDevServerHandler (handler)` - `useNitro()` (only usable after `ready` hook) - `addServerPlugin` -- `addPrerenderRoutes` +- `prerenderRoutes` ### Resolving diff --git a/packages/kit/src/nitro.ts b/packages/kit/src/nitro.ts index 9f46e41241..02f84eef6b 100644 --- a/packages/kit/src/nitro.ts +++ b/packages/kit/src/nitro.ts @@ -45,7 +45,7 @@ export function addServerPlugin (plugin: string) { /** * Adds routes to be prerendered */ -export function addPrerenderRoutes (routes: string | string[]) { +export function prerenderRoutes (routes: string | string[]) { const nuxt = useNuxt() if (!Array.isArray(routes)) { routes = [routes] diff --git a/packages/nuxt/src/app/components/nuxt-island.ts b/packages/nuxt/src/app/components/nuxt-island.ts index 0de8298589..6daaab8d26 100644 --- a/packages/nuxt/src/app/components/nuxt-island.ts +++ b/packages/nuxt/src/app/components/nuxt-island.ts @@ -11,7 +11,7 @@ import type { FetchResponse } from 'ofetch' import type { NuxtIslandResponse } from '../../core/runtime/nitro/renderer' import { getFragmentHTML, getSlotProps } from './utils' import { useNuxtApp, useRuntimeConfig } from '#app/nuxt' -import { useRequestEvent } from '#app/composables/ssr' +import { prerenderRoutes, useRequestEvent } from '#app/composables/ssr' // @ts-expect-error virtual file import { remoteComponentIslands } from '#build/nuxt.config.mjs' @@ -127,7 +127,7 @@ export default defineComponent({ if (import.meta.server && import.meta.prerender) { const hints = r.headers.get('x-nitro-prerender') if (hints) { - appendResponseHeader(event, 'x-nitro-prerender', hints) + prerenderRoutes(hints) } } setPayload(key, result) diff --git a/packages/nuxt/src/app/composables/index.ts b/packages/nuxt/src/app/composables/index.ts index 25a7b3b660..a815dbd05e 100644 --- a/packages/nuxt/src/app/composables/index.ts +++ b/packages/nuxt/src/app/composables/index.ts @@ -23,7 +23,7 @@ export { useFetch, useLazyFetch } from './fetch' export type { FetchResult, UseFetchOptions } from './fetch' export { useCookie } from './cookie' export type { CookieOptions, CookieRef } from './cookie' -export { useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus } from './ssr' +export { prerenderRoutes, useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus } from './ssr' export { onNuxtReady } from './ready' export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter } from './router' export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router' diff --git a/packages/nuxt/src/app/composables/ssr.ts b/packages/nuxt/src/app/composables/ssr.ts index f81b0d8d5c..69fc78dd5a 100644 --- a/packages/nuxt/src/app/composables/ssr.ts +++ b/packages/nuxt/src/app/composables/ssr.ts @@ -1,5 +1,5 @@ import type { H3Event } from 'h3' -import { setResponseStatus as _setResponseStatus, getRequestHeaders } from 'h3' +import { setResponseStatus as _setResponseStatus, appendHeader, getRequestHeaders } from 'h3' import type { NuxtApp } from '../nuxt' import { useNuxtApp } from '../nuxt' @@ -35,3 +35,10 @@ export function setResponseStatus (arg1: H3Event | number | undefined, arg2?: nu } return _setResponseStatus(useRequestEvent(), arg1, arg2 as string | undefined) } + +export function prerenderRoutes (path: string | string[]) { + if (!process.server || !process.env.prerender) { return } + + const paths = Array.isArray(path) ? path : [path] + appendHeader(useRequestEvent(), 'x-nitro-prerender', paths.map(p => encodeURIComponent(p)).join(', ')) +} diff --git a/packages/nuxt/src/imports/presets.ts b/packages/nuxt/src/imports/presets.ts index 8e3dab3bab..54b3eaedfe 100644 --- a/packages/nuxt/src/imports/presets.ts +++ b/packages/nuxt/src/imports/presets.ts @@ -37,6 +37,7 @@ const appPreset = defineUnimportPreset({ 'useRequestURL', 'setResponseStatus', 'setPageLayout', + 'prerenderRoutes', 'onNuxtReady', 'useRouter', 'useRoute', diff --git a/test/fixtures/basic/components/ServerOnlyComponent.server.vue b/test/fixtures/basic/components/ServerOnlyComponent.server.vue index e0c5945c1e..a775c99524 100644 --- a/test/fixtures/basic/components/ServerOnlyComponent.server.vue +++ b/test/fixtures/basic/components/ServerOnlyComponent.server.vue @@ -1,7 +1,5 @@