diff --git a/packages/start-plugin-core/src/prerender.ts b/packages/start-plugin-core/src/prerender.ts index 2fbd9e74b9..d044ecbe25 100644 --- a/packages/start-plugin-core/src/prerender.ts +++ b/packages/start-plugin-core/src/prerender.ts @@ -219,6 +219,7 @@ export async function prerender({ ) await new Promise((resolve) => setTimeout(resolve, retryDelay)) retriesByPath.set(page.path, retries + 1) + seen.delete(page.path) addCrawlPageTask(page) } else if (prerenderOptions.failOnError ?? true) { throw error diff --git a/packages/start-plugin-core/tests/prerender-retry.test.ts b/packages/start-plugin-core/tests/prerender-retry.test.ts new file mode 100644 index 0000000000..36090d103f --- /dev/null +++ b/packages/start-plugin-core/tests/prerender-retry.test.ts @@ -0,0 +1,68 @@ +import { describe, expect, it, vi } from 'vitest' +import { prerender } from '../src/prerender' + +vi.mock('../src/utils', async () => { + const actual = await vi.importActual('../src/utils') + return { + ...actual, + createLogger: () => ({ info: () => {}, warn: () => {}, error: () => {} }), + } +}) + +vi.mock('node:fs', async () => { + const actual = await vi.importActual('node:fs') + return { + ...actual, + promises: { + ...actual.promises, + mkdir: vi.fn().mockResolvedValue(undefined), + writeFile: vi.fn().mockResolvedValue(undefined), + }, + } +}) + +function htmlResponse() { + return new Response('', { + status: 200, + headers: { 'content-type': 'text/html' }, + }) +} + +function makeStartConfig(pagePath: string, prerenderOverrides: object = {}) { + return { + prerender: { + enabled: true, + autoStaticPathsDiscovery: false, + concurrency: 1, + ...prerenderOverrides, + }, + pages: [{ path: pagePath }], + router: { basepath: '' }, + spa: { + enabled: false, + prerender: { + outputPath: '/_shell', + crawlLinks: false, + retryCount: 0, + enabled: true, + }, + }, + } as any +} + +describe('prerender retries', () => { + it('re-fetches the page on retry', async () => { + const request = vi + .fn() + .mockRejectedValueOnce(new Error('connect ECONNREFUSED')) + .mockResolvedValue(htmlResponse()) + const handler = { getClientOutputDirectory: () => '/client', request } + const startConfig = makeStartConfig('/about', { + retryCount: 1, + retryDelay: 0, + }) + + await expect(prerender({ startConfig, handler })).resolves.not.toThrow() + expect(request).toHaveBeenCalledTimes(2) + }) +})