Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change config prerender.force to prerender.onError #2007

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tame-guests-collect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Remove the `prerender.force` option in favor of `prerender.onError`
27 changes: 25 additions & 2 deletions documentation/docs/14-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const config = {
prerender: {
crawl: true,
enabled: true,
force: false,
onError: 'fail',
pages: ['*']
},
router: true,
Expand Down Expand Up @@ -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` — 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

```ts
/** @type {import('@sveltejs/kit').PrerenderErrorHandler} */
const handleError = ({ status, path, referrer, referenceType }) => {
if (path.startsWith('/blog')) throw new Error('Missing a blog page!');
console.warn(`${status} ${path}${referrer ? ` (${referenceType} from ${referrer})` : ''}`);
};

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
Expand Down
51 changes: 38 additions & 13 deletions packages/kit/src/core/adapt/prerender.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import { mkdirp } from '../filesystem/index.js';
import { __fetch_polyfill } from '../../install-fetch.js';
import { SVELTE_KIT } from '../constants.js';

/**
* @typedef {import('types/config').PrerenderErrorHandler} PrerenderErrorHandler
* @typedef {import('types/config').PrerenderOnErrorValue} OnError
* @typedef {import('types/internal').Logger} Logger
*/

/** @param {string} html */
function clean_html(html) {
return html
Expand Down Expand Up @@ -45,13 +51,34 @@ function get_srcset_urls(attrs) {
return results;
}

/** @type {(errorDetails: Parameters<PrerenderErrorHandler>[0] ) => string} */
function errorDetailsToString({ status, path, referrer, referenceType }) {
return `${status} ${path}${referrer ? ` (${referenceType} from ${referrer})` : ''}`;
}

/** @type {(log: Logger, onError: OnError) => PrerenderErrorHandler} */
function chooseErrorHandler(log, onError) {
switch (onError) {
case 'continue':
return (errorDetails) => {
log.error(errorDetailsToString(errorDetails));
};
case 'fail':
return (errorDetails) => {
throw new Error(errorDetailsToString(errorDetails));
};
default:
return onError;
}
}

const OK = 2;
const REDIRECT = 3;

/** @param {{
* cwd: string;
* out: string;
* log: import('types/internal').Logger;
* log: Logger;
* config: import('types/config').ValidatedConfig;
* build_data: import('types/internal').BuildData;
* fallback?: string;
Expand All @@ -75,14 +102,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]);

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

/**
* @param {string} path
* @param {string?} parent
* @param {string?} referrer
*/
async function visit(path, parent) {
async function visit(path, referrer) {
path = normalize(path);

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

dependencies.forEach((result, dependency_path) => {
Expand All @@ -183,7 +203,12 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
if (response_type === OK) {
log.info(`${result.status} ${dependency_path}`);
} else {
error(result.status, dependency_path, path, 'fetched');
error({
status: result.status,
path: dependency_path,
referrer: path,
referenceType: 'fetched'
});
benmccann marked this conversation as resolved.
Show resolved Hide resolved
}
});

Expand Down
8 changes: 6 additions & 2 deletions packages/kit/src/core/config/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ test('fills in defaults', () => {
prerender: {
crawl: true,
enabled: true,
force: false,
// TODO: remove this for the 1.0 release
force: undefined,
onError: 'fail',
pages: ['*']
},
router: true,
Expand Down Expand Up @@ -150,7 +152,9 @@ test('fills in partial blanks', () => {
prerender: {
crawl: true,
enabled: true,
force: false,
// TODO: remove this for the 1.0 release
force: undefined,
onError: 'fail',
pages: ['*']
},
router: true,
Expand Down
28 changes: 27 additions & 1 deletion packages/kit/src/core/config/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,33 @@ const options = {
children: {
crawl: expect_boolean(true),
enabled: expect_boolean(true),
force: expect_boolean(false),
// TODO: remove this for the 1.0 release
force: {
happycollision marked this conversation as resolved.
Show resolved Hide resolved
type: 'leaf',
default: undefined,
validate: (option, keypath) => {
if (typeof option !== undefined) {
const newSetting = option ? 'continue' : 'fail';
const needsSetting = newSetting === 'continue';
throw new Error(
`${keypath} has been removed in favor of \`onError\`. In your case, set \`onError\` to "${newSetting}"${
needsSetting ? '' : ' (or leave it undefined)'
} to get the same behavior as you would with \`force: ${JSON.stringify(option)}\``
);
}
}
},
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: ['*'],
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/core/config/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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, force: undefined, onError: 'fail', pages: ['*'] },
router: true,
ssr: true,
target: null,
Expand Down
11 changes: 10 additions & 1 deletion packages/kit/types/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ export interface Config {
preprocess?: any;
}

export type PrerenderErrorHandler = (errorDetails: {
status: number;
path: string;
referrer: string | null;
referenceType: 'linked' | 'fetched';
}) => void | never;

export type PrerenderOnErrorValue = 'fail' | 'continue' | PrerenderErrorHandler;

export interface ValidatedConfig {
compilerOptions: any;
extensions: string[];
Expand Down Expand Up @@ -117,7 +126,7 @@ export interface ValidatedConfig {
prerender: {
crawl: boolean;
enabled: boolean;
force: boolean;
onError: PrerenderOnErrorValue;
pages: string[];
};
router: boolean;
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down