/** | |
* A helper function for sequencing multiple `handle` calls in a middleware-like manner. | |
* The behavior for the `handle` options is as follows: | |
* - `transformPageChunk` is applied in reverse order and merged | |
* - `preload` is applied in forward order, the first option "wins" and no `preload` options after it are called | |
* - `filterSerializedResponseHeaders` behaves the same as `preload` | |
* | |
* ```js | |
* /// file: src/hooks.server.js | |
* import { sequence } from '@sveltejs/kit/hooks'; | |
* | |
* /// type: import('@sveltejs/kit').Handle | |
* async function first({ event, resolve }) { | |
* console.log('first pre-processing'); | |
* const result = await resolve(event, { | |
* transformPageChunk: ({ html }) => { | |
* // transforms are applied in reverse order | |
* console.log('first transform'); | |
* return html; | |
* }, | |
* preload: () => { | |
* // this one wins as it's the first defined in the chain | |
* console.log('first preload'); | |
* } | |
* }); | |
* console.log('first post-processing'); | |
* return result; | |
* } | |
* | |
* /// type: import('@sveltejs/kit').Handle | |
* async function second({ event, resolve }) { | |
* console.log('second pre-processing'); | |
* const result = await resolve(event, { | |
* transformPageChunk: ({ html }) => { | |
* console.log('second transform'); | |
* return html; | |
* }, | |
* preload: () => { | |
* console.log('second preload'); | |
* }, | |
* filterSerializedResponseHeaders: () => { | |
* // this one wins as it's the first defined in the chain | |
* console.log('second filterSerializedResponseHeaders'); | |
* } | |
* }); | |
* console.log('second post-processing'); | |
* return result; | |
* } | |
* | |
* export const handle = sequence(first, second); | |
* ``` | |
* | |
* The example above would print: | |
* | |
* ``` | |
* first pre-processing | |
* first preload | |
* second pre-processing | |
* second filterSerializedResponseHeaders | |
* second transform | |
* first transform | |
* second post-processing | |
* first post-processing | |
* ``` | |
* | |
* @param {...import('@sveltejs/kit').Handle} handlers The chain of `handle` functions | |
* @returns {import('@sveltejs/kit').Handle} | |
*/ | |
export function sequence(...handlers) { | |
const length = handlers.length; | |
if (!length) return ({ event, resolve }) => resolve(event); | |
return ({ event, resolve }) => { | |
return apply_handle(0, event, {}); | |
/** | |
* @param {number} i | |
* @param {import('@sveltejs/kit').RequestEvent} event | |
* @param {import('@sveltejs/kit').ResolveOptions | undefined} parent_options | |
* @returns {import('types').MaybePromise<Response>} | |
*/ | |
function apply_handle(i, event, parent_options) { | |
const handle = handlers[i]; | |
return handle({ | |
event, | |
resolve: (event, options) => { | |
/** @type {import('@sveltejs/kit').ResolveOptions['transformPageChunk']} */ | |
const transformPageChunk = async ({ html, done }) => { | |
if (options?.transformPageChunk) { | |
html = (await options.transformPageChunk({ html, done })) ?? ''; | |
} | |
if (parent_options?.transformPageChunk) { | |
html = (await parent_options.transformPageChunk({ html, done })) ?? ''; | |
} | |
return html; | |
}; | |
/** @type {import('@sveltejs/kit').ResolveOptions['filterSerializedResponseHeaders']} */ | |
const filterSerializedResponseHeaders = | |
parent_options?.filterSerializedResponseHeaders ?? | |
options?.filterSerializedResponseHeaders; | |
/** @type {import('@sveltejs/kit').ResolveOptions['preload']} */ | |
const preload = parent_options?.preload ?? options?.preload; | |
return i < length - 1 | |
? apply_handle(i + 1, event, { | |
transformPageChunk, | |
filterSerializedResponseHeaders, | |
preload | |
}) | |
: resolve(event, { transformPageChunk, filterSerializedResponseHeaders, preload }); | |
} | |
}); | |
} | |
}; | |
} | |