Skip to content

Commit b0addbf

Browse files
Change config prerender.force to prerender.onError
fixes #1940 - Changing from `force` to `onError` gives a somewhat more descriptive name to the option. - Using the strings "fail" and "continue" further clarifies what the option does. - Providing your own function to deal with an error allows for fine-tuning which errors fail the build and which do not. The original ticket suggested that the custom function return a boolean, but after seeing the implementation of the error handler in svelteKit, I thought it more fitting to just allow it the exact same API: throw if you want the build to fail.
1 parent 68c3906 commit b0addbf

File tree

8 files changed

+91
-21
lines changed

8 files changed

+91
-21
lines changed

.changeset/tame-guests-collect.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
Remove the `prerender.force` option in favor of `prerender.onError`

documentation/docs/14-configuration.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const config = {
4848
prerender: {
4949
crawl: true,
5050
enabled: true,
51-
force: false,
51+
onError: 'fail',
5252
pages: ['*']
5353
},
5454
router: true,
@@ -148,7 +148,30 @@ See [Prerendering](#ssr-and-javascript-prerender). An object containing zero or
148148

149149
- `crawl` — determines whether SvelteKit should find pages to prerender by following links from the seed page(s)
150150
- `enabled` — set to `false` to disable prerendering altogether
151-
- `force` — if `true`, a page that fails to render will _not_ cause the entire build to fail
151+
- `onError`
152+
153+
- `'fail'` — (default) fails the build when a routing error is encountered when following a link
154+
- `'continue'` — allows the build to continue, despite routing errors
155+
- `function` — custom error handler allowing you to log, `throw` and fail the build, or take other action of your choosing based on the details of the crawl
156+
157+
```ts
158+
/** @type {import('@sveltejs/kit').PrerenderErrorHandler} */
159+
const handleError = ({ status, path, referrer, referenceType }) => {
160+
if (path.startsWith('/blog')) throw new Error('Missing a blog page!');
161+
console.warn(`${status} ${path}${referrer ? ` (${referenceType} from ${referrer})` : ''}`);
162+
};
163+
164+
export default {
165+
kit: {
166+
adapter: static(),
167+
target: '#svelte',
168+
prerender: {
169+
onError: handleError
170+
}
171+
}
172+
};
173+
```
174+
152175
- `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]` )
153176

154177
### router

packages/kit/src/core/adapt/prerender.js

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import { mkdirp } from '../filesystem/index.js';
55
import { __fetch_polyfill } from '../../install-fetch.js';
66
import { SVELTE_KIT } from '../constants.js';
77

8+
/**
9+
* @typedef {import('types/config').PrerenderErrorHandler} PrerenderErrorHandler
10+
* @typedef {import('types/config').ValidatedConfig['kit']['prerender']['onError']} OnErrorConfig
11+
* @typedef {import('types/internal').Logger} Logger
12+
*/
13+
814
/** @param {string} html */
915
function clean_html(html) {
1016
return html
@@ -45,13 +51,34 @@ function get_srcset_urls(attrs) {
4551
return results;
4652
}
4753

54+
/** @type {(errorDetails: Parameters<PrerenderErrorHandler>[0] ) => string} */
55+
function errorDetailsToString({ status, path, referrer, referenceType }) {
56+
return `${status} ${path}${referrer ? ` (${referenceType} from ${referrer})` : ''}`;
57+
}
58+
59+
/** @type {(log: Logger, onError: OnErrorConfig) => PrerenderErrorHandler} */
60+
function chooseErrorHandler(log, onError) {
61+
switch (onError) {
62+
case 'continue':
63+
return (errorDetails) => {
64+
log.error(errorDetailsToString(errorDetails));
65+
};
66+
case 'fail':
67+
return (errorDetails) => {
68+
throw new Error(errorDetailsToString(errorDetails));
69+
};
70+
default:
71+
return onError;
72+
}
73+
}
74+
4875
const OK = 2;
4976
const REDIRECT = 3;
5077

5178
/** @param {{
5279
* cwd: string;
5380
* out: string;
54-
* log: import('types/internal').Logger;
81+
* log: Logger;
5582
* config: import('types/config').ValidatedConfig;
5683
* build_data: import('types/internal').BuildData;
5784
* fallback?: string;
@@ -75,14 +102,7 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
75102
read: (file) => readFileSync(join(config.kit.files.assets, file))
76103
});
77104

78-
/** @type {(status: number, path: string, parent: string | null, verb: string) => void} */
79-
const error = config.kit.prerender.force
80-
? (status, path, parent, verb) => {
81-
log.error(`${status} ${path}${parent ? ` (${verb} from ${parent})` : ''}`);
82-
}
83-
: (status, path, parent, verb) => {
84-
throw new Error(`${status} ${path}${parent ? ` (${verb} from ${parent})` : ''}`);
85-
};
105+
const error = chooseErrorHandler(log, config.kit.prerender.onError);
86106

87107
const files = new Set([...build_data.static, ...build_data.client]);
88108

@@ -107,9 +127,9 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
107127

108128
/**
109129
* @param {string} path
110-
* @param {string?} parent
130+
* @param {string?} referrer
111131
*/
112-
async function visit(path, parent) {
132+
async function visit(path, referrer) {
113133
path = normalize(path);
114134

115135
if (seen.has(path)) return;
@@ -162,7 +182,7 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
162182
log.info(`${rendered.status} ${path}`);
163183
writeFileSync(file, rendered.body || '');
164184
} else if (response_type !== OK) {
165-
error(rendered.status, path, parent, 'linked');
185+
error({ status: rendered.status, path, referrer, referenceType: 'linked' });
166186
}
167187

168188
dependencies.forEach((result, dependency_path) => {
@@ -183,7 +203,12 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
183203
if (response_type === OK) {
184204
log.info(`${result.status} ${dependency_path}`);
185205
} else {
186-
error(result.status, dependency_path, path, 'fetched');
206+
error({
207+
status: result.status,
208+
path: dependency_path,
209+
referrer: path,
210+
referenceType: 'fetched'
211+
});
187212
}
188213
});
189214

packages/kit/src/core/config/index.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ test('fills in defaults', () => {
5050
prerender: {
5151
crawl: true,
5252
enabled: true,
53-
force: false,
53+
onError: 'fail',
5454
pages: ['*']
5555
},
5656
router: true,
@@ -150,7 +150,7 @@ test('fills in partial blanks', () => {
150150
prerender: {
151151
crawl: true,
152152
enabled: true,
153-
force: false,
153+
onError: 'fail',
154154
pages: ['*']
155155
},
156156
router: true,

packages/kit/src/core/config/options.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,17 @@ const options = {
121121
children: {
122122
crawl: expect_boolean(true),
123123
enabled: expect_boolean(true),
124-
force: expect_boolean(false),
124+
onError: {
125+
type: 'leaf',
126+
default: 'fail',
127+
validate: (option, keypath) => {
128+
if (typeof option === 'function') return option;
129+
if (['continue', 'fail'].includes(option)) return option;
130+
throw new Error(
131+
`${keypath} should be either a custom function or one of "continue" or "fail"`
132+
);
133+
}
134+
},
125135
pages: {
126136
type: 'leaf',
127137
default: ['*'],

packages/kit/src/core/config/test/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ async function testLoadDefaultConfig(path) {
5454
exclude: []
5555
},
5656
paths: { base: '', assets: '/.' },
57-
prerender: { crawl: true, enabled: true, force: false, pages: ['*'] },
57+
prerender: { crawl: true, enabled: true, onError: 'fail', pages: ['*'] },
5858
router: true,
5959
ssr: true,
6060
target: null,

packages/kit/types/config.d.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ export type Config = {
7878
preprocess?: any;
7979
};
8080

81+
export type PrerenderErrorHandler = (errorDetails: {
82+
status: number;
83+
path: string;
84+
referrer: string | null;
85+
referenceType: 'linked' | 'fetched';
86+
}) => void | never;
87+
8188
export type ValidatedConfig = {
8289
compilerOptions: any;
8390
extensions: string[];
@@ -117,7 +124,7 @@ export type ValidatedConfig = {
117124
prerender: {
118125
crawl: boolean;
119126
enabled: boolean;
120-
force: boolean;
127+
onError: 'fail' | 'continue' | PrerenderErrorHandler;
121128
pages: string[];
122129
};
123130
router: boolean;

packages/kit/types/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import './ambient-modules';
55

6-
export { Adapter, AdapterUtils, Config, ValidatedConfig } from './config';
6+
export { Adapter, AdapterUtils, Config, ValidatedConfig, PrerenderErrorHandler } from './config';
77
export { EndpointOutput, RequestHandler } from './endpoint';
88
export { ErrorLoad, ErrorLoadInput, Load, LoadInput, LoadOutput, Page } from './page';
99
export {

0 commit comments

Comments
 (0)