diff --git a/.changeset/tame-guests-collect.md b/.changeset/tame-guests-collect.md new file mode 100644 index 0000000000000..bac58d0d52b21 --- /dev/null +++ b/.changeset/tame-guests-collect.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Remove the `prerender.force` option in favor of `prerender.onError` diff --git a/documentation/docs/14-configuration.md b/documentation/docs/14-configuration.md index 43a45fd9a5f51..ffb6cc07ae160 100644 --- a/documentation/docs/14-configuration.md +++ b/documentation/docs/14-configuration.md @@ -48,7 +48,7 @@ const config = { prerender: { crawl: true, enabled: true, - force: false, + onError: 'fail', pages: ['*'] }, router: true, @@ -148,7 +148,30 @@ See [Prerendering](#ssr-and-javascript-prerender). An object containing zero or - `crawl` — determines whether SvelteKit should find pages to prerender by following links from the seed page(s) - `enabled` — set to `false` to disable prerendering altogether -- `force` — if `true`, a page that fails to render will _not_ cause the entire build to fail +- `onError` + + - `"fail"` — (default) fails the build when a routing error is encountered when following a link + - `"continue"` — allows the build to continue, despite routing errors + - function — you can use the `errorDetails` to determine whether or not to `throw`, thereby failing the build. + + ```ts + /** @type {import('@sveltejs/kit').PrerenderErrorHandler} */ + const handleError = ({ status, path, parent, verb }) => { + if (path.startsWith('/blog')) throw new Error('Missing a blog page!'); + console.warn(`${status} ${path}${parent ? ` (${verb} from ${parent})` : ''}`); + + export default { + kit: { + adapter: static(), + target: '#svelte', + prerender: { + onError: handleError + } + } + }; + }; + ``` + - `pages` — an array of pages to prerender, or start crawling from (if `crawl: true`). The `*` string includes all non-dynamic routes (i.e. pages with no `[parameters]` ) ### router diff --git a/packages/kit/src/core/adapt/prerender.js b/packages/kit/src/core/adapt/prerender.js index 9721c91db0fa3..4463cca435f3e 100644 --- a/packages/kit/src/core/adapt/prerender.js +++ b/packages/kit/src/core/adapt/prerender.js @@ -45,6 +45,32 @@ function get_srcset_urls(attrs) { return results; } +/** @typedef {((status: number, path: string, parent: string | null, verb: string) => void | never)} InternalErrorHandler */ + +/** @type {(...details: Parameters ) => string} */ +function errorDetailsToString(status, path, referrer, verb) { + return `${status} ${path}${referrer ? ` (${verb} from ${referrer})` : ''}`; +} + +/** @type {(log: import('types/internal').Logger, onError: import('types/config').ValidatedConfig['kit']['prerender']['onError']) => InternalErrorHandler} */ +function chooseErrorHandler(log, onError) { + switch (onError) { + case 'continue': + /** @type {InternalErrorHandler} */ + return (status, path, referrer, verb) => { + log.error(errorDetailsToString(status, path, referrer, verb)); + }; + case 'fail': + /** @type {InternalErrorHandler} */ + return (status, path, referrer, verb) => { + throw new Error(errorDetailsToString(status, path, referrer, verb)); + }; + default: + /** @type {InternalErrorHandler} */ + return (status, path, referrer, verb) => onError({ status, path, referrer, verb }); + } +} + const OK = 2; const REDIRECT = 3; @@ -75,14 +101,7 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a read: (file) => readFileSync(join(config.kit.files.assets, file)) }); - /** @type {(status: number, path: string, parent: string | null, verb: string) => void} */ - const error = config.kit.prerender.force - ? (status, path, parent, verb) => { - log.error(`${status} ${path}${parent ? ` (${verb} from ${parent})` : ''}`); - } - : (status, path, parent, verb) => { - throw new Error(`${status} ${path}${parent ? ` (${verb} from ${parent})` : ''}`); - }; + const error = chooseErrorHandler(log, config.kit.prerender.onError); const files = new Set([...build_data.static, ...build_data.client]); diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js index f0954248b95d2..e6aea5473ec22 100644 --- a/packages/kit/src/core/config/index.spec.js +++ b/packages/kit/src/core/config/index.spec.js @@ -50,7 +50,7 @@ test('fills in defaults', () => { prerender: { crawl: true, enabled: true, - force: false, + onError: 'fail', pages: ['*'] }, router: true, @@ -150,7 +150,7 @@ test('fills in partial blanks', () => { prerender: { crawl: true, enabled: true, - force: false, + onError: 'fail', pages: ['*'] }, router: true, diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js index 633c3ad75c843..89f8b35040e22 100644 --- a/packages/kit/src/core/config/options.js +++ b/packages/kit/src/core/config/options.js @@ -121,7 +121,17 @@ const options = { children: { crawl: expect_boolean(true), enabled: expect_boolean(true), - force: expect_boolean(false), + onError: { + type: 'leaf', + default: 'fail', + validate: (option, keypath) => { + if (typeof option === 'function') return option; + if (['continue', 'fail'].includes(option)) return option; + throw new Error( + `${keypath} should be either a custom function or one of "continue" or "fail"` + ); + } + }, pages: { type: 'leaf', default: ['*'], diff --git a/packages/kit/src/core/config/test/index.js b/packages/kit/src/core/config/test/index.js index daa1c8b400b75..84d5fc784a49b 100644 --- a/packages/kit/src/core/config/test/index.js +++ b/packages/kit/src/core/config/test/index.js @@ -54,7 +54,7 @@ async function testLoadDefaultConfig(path) { exclude: [] }, paths: { base: '', assets: '/.' }, - prerender: { crawl: true, enabled: true, force: false, pages: ['*'] }, + prerender: { crawl: true, enabled: true, onError: 'fail', pages: ['*'] }, router: true, ssr: true, target: null, diff --git a/packages/kit/types/config.d.ts b/packages/kit/types/config.d.ts index 45482dddb12fe..b101a28ec86e2 100644 --- a/packages/kit/types/config.d.ts +++ b/packages/kit/types/config.d.ts @@ -78,6 +78,13 @@ export type Config = { preprocess?: any; }; +export type PrerenderErrorHandler = (errorDetails: { + status: number; + path: string; + referrer: string | null; + verb: string; +}) => void | never; + export type ValidatedConfig = { compilerOptions: any; extensions: string[]; @@ -117,7 +124,7 @@ export type ValidatedConfig = { prerender: { crawl: boolean; enabled: boolean; - force: boolean; + onError: 'fail' | 'continue' | PrerenderErrorHandler; pages: string[]; }; router: boolean; diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index be0495bd1efb4..683031a2dc781 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -3,7 +3,7 @@ import './ambient-modules'; -export { Adapter, AdapterUtils, Config, ValidatedConfig } from './config'; +export { Adapter, AdapterUtils, Config, ValidatedConfig, PrerenderErrorHandler } from './config'; export { EndpointOutput, RequestHandler } from './endpoint'; export { ErrorLoad, ErrorLoadInput, Load, LoadInput, LoadOutput, Page } from './page'; export {