fix(nuxt): hint prerenderer to crawl routes at runtime (#26694)

This commit is contained in:
Ryan Clements 2024-04-12 18:21:02 -04:00 committed by GitHub
parent cc391c0448
commit b7ba3a4a80
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 57 additions and 34 deletions

View File

@ -507,9 +507,12 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
if (nitro.options.static) {
nitro.hooks.hook('prerender:routes', (routes) => {
for (const route of [nuxt.options.ssr ? '/' : '/index.html', '/200.html', '/404.html']) {
for (const route of ['/200.html', '/404.html']) {
routes.add(route)
}
if (!nuxt.options.ssr) {
routes.add('/index.html')
}
})
}

View File

@ -3,7 +3,6 @@ import { mkdir, readFile } from 'node:fs/promises'
import { addBuildPlugin, addComponent, addPlugin, addTemplate, addTypeTemplate, addVitePlugin, addWebpackPlugin, defineNuxtModule, findPath, logger, updateTemplates, useNitro } from '@nuxt/kit'
import { dirname, join, relative, resolve } from 'pathe'
import { genImport, genObjectFromRawEntries, genString } from 'knitwork'
import { joinURL } from 'ufo'
import type { Nuxt, NuxtApp, NuxtPage } from 'nuxt/schema'
import { createRoutesContext } from 'unplugin-vue-router'
import { resolveOptions } from 'unplugin-vue-router/options'
@ -18,8 +17,6 @@ import type { PageMetaPluginOptions } from './plugins/page-meta'
import { PageMetaPlugin } from './plugins/page-meta'
import { RouteInjectionPlugin } from './plugins/route-injection'
const OPTIONAL_PARAM_RE = /^\/?:.*(\?|\(\.\*\)\*)$/
export default defineNuxtModule({
meta: {
name: 'pages',
@ -266,36 +263,14 @@ export default defineNuxtModule({
})
})
nuxt.hook('nitro:init', (nitro) => {
if (nuxt.options.dev || !nitro.options.static || nuxt.options.router.options.hashMode) { return }
// Prerender all non-dynamic page routes when generating app
const prerenderRoutes = new Set<string>()
nuxt.hook('pages:extend', (pages) => {
prerenderRoutes.clear()
const processPages = (pages: NuxtPage[], currentPath = '/') => {
for (const page of pages) {
// Add root of optional dynamic paths and catchalls
if (OPTIONAL_PARAM_RE.test(page.path) && !page.children?.length) { prerenderRoutes.add(currentPath) }
// Skip dynamic paths
if (page.path.includes(':')) { continue }
const route = joinURL(currentPath, page.path)
prerenderRoutes.add(route)
if (page.children) { processPages(page.children, route) }
}
}
processPages(pages)
})
nuxt.hook('nitro:build:before', (nitro) => {
if (nitro.options.prerender.routes.length) {
for (const route of nitro.options.prerender.routes) {
// Skip default route value as we only generate it if it is already
// in the detected routes from `~/pages`.
if (route === '/') { continue }
prerenderRoutes.add(route)
}
}
nitro.options.prerender.routes = Array.from(prerenderRoutes)
})
nuxt.hook('app:resolve', (app) => {
const nitro = useNitro()
if (nitro.options.prerender.crawlLinks) {
app.plugins.push({
src: resolve(runtimeDir, 'plugins/prerender.server'),
mode: 'server',
})
}
})
nuxt.hook('imports:extend', (imports) => {

View File

@ -0,0 +1,45 @@
import type { RouteRecordRaw } from 'vue-router'
import { joinURL } from 'ufo'
import { defineNuxtPlugin } from '#app/nuxt'
import { prerenderRoutes } from '#app/composables/ssr'
// @ts-expect-error virtual file
import _routes from '#build/routes'
// @ts-expect-error virtual file
import routerOptions from '#build/router.options'
let routes: string[]
export default defineNuxtPlugin(async () => {
if (!import.meta.server || !import.meta.prerender || routerOptions.hashMode) {
return
}
if (routes && !routes.length) { return }
routes ||= Array.from(processRoutes(await routerOptions.routes?.(_routes) ?? _routes))
const batch = routes.splice(0, 10)
prerenderRoutes(batch)
})
// Implementation
const OPTIONAL_PARAM_RE = /^\/?:.*(\?|\(\.\*\)\*)$/
function processRoutes (routes: RouteRecordRaw[], currentPath = '/', routesToPrerender = new Set<string>()) {
for (const route of routes) {
// Add root of optional dynamic paths and catchalls
if (OPTIONAL_PARAM_RE.test(route.path) && !route.children?.length) {
routesToPrerender.add(currentPath)
}
// Skip dynamic paths
if (route.path.includes(':')) {
continue
}
const fullPath = joinURL(currentPath, route.path)
routesToPrerender.add(fullPath)
if (route.children) {
processRoutes(route.children, fullPath, routesToPrerender)
}
}
return routesToPrerender
}