Skip to content

Commit 20dad18

Browse files
Change config prerender.force to prerender.onError (#2007)
1 parent d656316 commit 20dad18

File tree

8 files changed

+113
-21
lines changed

8 files changed

+113
-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').PrerenderOnErrorValue} OnError
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: OnError) => 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: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ test('fills in defaults', () => {
5050
prerender: {
5151
crawl: true,
5252
enabled: true,
53-
force: false,
53+
// TODO: remove this for the 1.0 release
54+
force: undefined,
55+
onError: 'fail',
5456
pages: ['*']
5557
},
5658
router: true,
@@ -150,7 +152,9 @@ test('fills in partial blanks', () => {
150152
prerender: {
151153
crawl: true,
152154
enabled: true,
153-
force: false,
155+
// TODO: remove this for the 1.0 release
156+
force: undefined,
157+
onError: 'fail',
154158
pages: ['*']
155159
},
156160
router: true,

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,33 @@ const options = {
121121
children: {
122122
crawl: expect_boolean(true),
123123
enabled: expect_boolean(true),
124-
force: expect_boolean(false),
124+
// TODO: remove this for the 1.0 release
125+
force: {
126+
type: 'leaf',
127+
default: undefined,
128+
validate: (option, keypath) => {
129+
if (typeof option !== undefined) {
130+
const newSetting = option ? 'continue' : 'fail';
131+
const needsSetting = newSetting === 'continue';
132+
throw new Error(
133+
`${keypath} has been removed in favor of \`onError\`. In your case, set \`onError\` to "${newSetting}"${
134+
needsSetting ? '' : ' (or leave it undefined)'
135+
} to get the same behavior as you would with \`force: ${JSON.stringify(option)}\``
136+
);
137+
}
138+
}
139+
},
140+
onError: {
141+
type: 'leaf',
142+
default: 'fail',
143+
validate: (option, keypath) => {
144+
if (typeof option === 'function') return option;
145+
if (['continue', 'fail'].includes(option)) return option;
146+
throw new Error(
147+
`${keypath} should be either a custom function or one of "continue" or "fail"`
148+
);
149+
}
150+
},
125151
pages: {
126152
type: 'leaf',
127153
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, force: undefined, onError: 'fail', pages: ['*'] },
5858
router: true,
5959
ssr: true,
6060
target: null,

packages/kit/types/config.d.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ export interface 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+
88+
export type PrerenderOnErrorValue = 'fail' | 'continue' | PrerenderErrorHandler;
89+
8190
export interface ValidatedConfig {
8291
compilerOptions: any;
8392
extensions: string[];
@@ -117,7 +126,7 @@ export interface ValidatedConfig {
117126
prerender: {
118127
crawl: boolean;
119128
enabled: boolean;
120-
force: boolean;
129+
onError: PrerenderOnErrorValue;
121130
pages: string[];
122131
};
123132
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)