diff --git a/packages/void/src/prerender-paths.ts b/packages/void/src/prerender-paths.ts index 0d0715d15..587fc765e 100644 --- a/packages/void/src/prerender-paths.ts +++ b/packages/void/src/prerender-paths.ts @@ -65,8 +65,8 @@ export async function collectPrerenderPaths(options: { } } } catch (err) { - console.warn( - `[void] Failed to resolve prerender paths for ${page.pattern}: ${err instanceof Error ? err.message : String(err)}`, + throw new Error( + `Failed to resolve prerender paths for ${page.pattern}: ${err instanceof Error ? err.message : String(err)}`, ); } } @@ -97,8 +97,8 @@ export async function collectPrerenderPaths(options: { } } } catch (err) { - console.warn( - `[void] Failed to resolve SSR prerender paths: ${err instanceof Error ? err.message : String(err)}`, + throw new Error( + `Failed to resolve SSR prerender paths: ${err instanceof Error ? err.message : String(err)}`, ); } finally { await viteServer.close(); diff --git a/packages/void/test/integration/static-pages.test.ts b/packages/void/test/integration/static-pages.test.ts index c37e2e35d..b83ba5f59 100644 --- a/packages/void/test/integration/static-pages.test.ts +++ b/packages/void/test/integration/static-pages.test.ts @@ -73,6 +73,18 @@ describe("playground/static-pages build (output: static)", () => { expect(html).toContain("Post hello"); }); + it("prerenders dynamic pages with getPrerenderPaths using virtual modules", () => { + // tags/[tag] exports getPrerenderPaths that imports from virtual:tags plugin + const vuePath = join(DIST, "client/tags/vue.html"); + const vitePath = join(DIST, "client/tags/vite.html"); + expect(existsSync(vuePath)).toBe(true); + expect(existsSync(vitePath)).toBe(true); + + const html = readFileSync(vuePath, "utf-8"); + expect(html).toContain('data-testid="tag-title"'); + expect(html).toContain("Tag vue"); + }); + it("does not prerender dynamic pages without getPrerenderPaths", () => { // items/[id] has no getPrerenderPaths, so it should NOT be prerendered expect(existsSync(join(DIST, "client/items"))).toBe(false); diff --git a/playground/static-pages/pages/tags/[tag].server.ts b/playground/static-pages/pages/tags/[tag].server.ts new file mode 100644 index 000000000..dc61013be --- /dev/null +++ b/playground/static-pages/pages/tags/[tag].server.ts @@ -0,0 +1,16 @@ +import { defineHandler } from "void"; +import { tags } from "virtual:tags"; + +export interface Props { + tag: string; +} + +export const prerender = true; + +export function getPrerenderPaths() { + return tags.map((tag: string) => ({ tag })); +} + +export const loader = defineHandler((c) => { + return { tag: c.req.param("tag") }; +}); diff --git a/playground/static-pages/pages/tags/[tag].vue b/playground/static-pages/pages/tags/[tag].vue new file mode 100644 index 000000000..d96eee207 --- /dev/null +++ b/playground/static-pages/pages/tags/[tag].vue @@ -0,0 +1,9 @@ + + + diff --git a/playground/static-pages/vite.config.ts b/playground/static-pages/vite.config.ts index 44c74f311..02abc9b1c 100644 --- a/playground/static-pages/vite.config.ts +++ b/playground/static-pages/vite.config.ts @@ -1,7 +1,23 @@ -import { defineConfig } from "vite"; +import { defineConfig, type Plugin } from "vite"; import { voidPlugin } from "void"; import { voidVue } from "@void-x/vue"; +function virtualTagsPlugin(): Plugin { + const VIRTUAL_ID = "virtual:tags"; + const RESOLVED_ID = "\0virtual:tags"; + return { + name: "virtual-tags", + resolveId(id) { + if (id === VIRTUAL_ID) return RESOLVED_ID; + }, + load(id) { + if (id === RESOLVED_ID) { + return `export const tags = ["vue", "vite"];`; + } + }, + }; +} + export default defineConfig({ - plugins: [voidPlugin(), voidVue()], + plugins: [voidPlugin(), voidVue(), virtualTagsPlugin()], });