From aa76bdc25f3149717cd2e019c29da961fb87a390 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 30 Aug 2022 10:36:07 -0400 Subject: [PATCH] [breaking] merge `resolve` options when using `sequence` (#6401) Closes #4051 --- .changeset/red-rice-bathe.md | 5 + packages/kit/src/exports/hooks/sequence.js | 27 ++++- .../kit/src/exports/hooks/sequence.spec.js | 106 ++++++++++++++++++ packages/kit/types/ambient.d.ts | 17 ++- 4 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 .changeset/red-rice-bathe.md create mode 100644 packages/kit/src/exports/hooks/sequence.spec.js diff --git a/.changeset/red-rice-bathe.md b/.changeset/red-rice-bathe.md new file mode 100644 index 000000000000..e87fd387e679 --- /dev/null +++ b/.changeset/red-rice-bathe.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[breaking] merge resolve options when using sequence helper diff --git a/packages/kit/src/exports/hooks/sequence.js b/packages/kit/src/exports/hooks/sequence.js index e45f2895355d..c65afca9eb1d 100644 --- a/packages/kit/src/exports/hooks/sequence.js +++ b/packages/kit/src/exports/hooks/sequence.js @@ -7,19 +7,40 @@ export function sequence(...handlers) { if (!length) return ({ event, resolve }) => resolve(event); return ({ event, resolve }) => { - return apply_handle(0, event); + return apply_handle(0, event, {}); /** * @param {number} i * @param {import('types').RequestEvent} event + * @param {import('types').ResolveOptions | undefined} parent_options * @returns {import('types').MaybePromise} */ - function apply_handle(i, event) { + function apply_handle(i, event, parent_options) { const handle = handlers[i]; return handle({ event, - resolve: i < length - 1 ? (event) => apply_handle(i + 1, event) : resolve + resolve: (event, options) => { + /** @param {{ html: string, done: boolean }} opts */ + 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; + }; + + // TODO remove post-https://github.com/sveltejs/kit/pull/6197 + const ssr = options?.ssr ?? parent_options?.ssr; + + return i < length - 1 + ? apply_handle(i + 1, event, { transformPageChunk, ssr }) + : resolve(event, { transformPageChunk, ssr }); + } }); } }; diff --git a/packages/kit/src/exports/hooks/sequence.spec.js b/packages/kit/src/exports/hooks/sequence.spec.js new file mode 100644 index 000000000000..1706bee60fc2 --- /dev/null +++ b/packages/kit/src/exports/hooks/sequence.spec.js @@ -0,0 +1,106 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { sequence } from './sequence.js'; +import { installPolyfills } from '../node/polyfills.js'; + +installPolyfills(); + +test('applies handlers in sequence', async () => { + /** @type {string[]} */ + const order = []; + + const handler = sequence( + async ({ event, resolve }) => { + order.push('1a'); + const response = await resolve(event); + order.push('1b'); + return response; + }, + async ({ event, resolve }) => { + order.push('2a'); + const response = await resolve(event); + order.push('2b'); + return response; + }, + async ({ event, resolve }) => { + order.push('3a'); + const response = await resolve(event); + order.push('3b'); + return response; + } + ); + + const event = /** @type {import('types').RequestEvent} */ ({}); + const response = new Response(); + + assert.equal(await handler({ event, resolve: () => response }), response); + assert.equal(order, ['1a', '2a', '3a', '3b', '2b', '1b']); +}); + +test('uses transformPageChunk option passed to non-terminal handle function', async () => { + const handler = sequence( + async ({ event, resolve }) => { + return resolve(event, { + transformPageChunk: ({ html, done }) => html + (done ? '-1-done' : '-1') + }); + }, + async ({ event, resolve }) => resolve(event), + async ({ event, resolve }) => resolve(event) + ); + + const event = /** @type {import('types').RequestEvent} */ ({}); + const response = await handler({ + event, + resolve: async (_event, opts = {}) => { + let html = ''; + + const { transformPageChunk = ({ html }) => html } = opts; + + html += await transformPageChunk({ html: '0', done: false }); + html += await transformPageChunk({ html: ' 0', done: true }); + + return new Response(html); + } + }); + + assert.equal(await response.text(), '0-1 0-1-done'); +}); + +test('merges transformPageChunk option', async () => { + const handler = sequence( + async ({ event, resolve }) => { + return resolve(event, { + transformPageChunk: ({ html, done }) => html + (done ? '-1-done' : '-1') + }); + }, + async ({ event, resolve }) => { + return resolve(event, { + transformPageChunk: ({ html, done }) => html + (done ? '-2-done' : '-2') + }); + }, + async ({ event, resolve }) => { + return resolve(event, { + transformPageChunk: ({ html, done }) => html + (done ? '-3-done' : '-3') + }); + } + ); + + const event = /** @type {import('types').RequestEvent} */ ({}); + const response = await handler({ + event, + resolve: async (_event, opts = {}) => { + let html = ''; + + const { transformPageChunk = ({ html }) => html } = opts; + + html += await transformPageChunk({ html: '0', done: false }); + html += await transformPageChunk({ html: ' 0', done: true }); + + return new Response(html); + } + }); + + assert.equal(await response.text(), '0-3-2-1 0-3-done-2-done-1-done'); +}); + +test.run(); diff --git a/packages/kit/types/ambient.d.ts b/packages/kit/types/ambient.d.ts index 68b4da04c306..15d7421507bb 100644 --- a/packages/kit/types/ambient.d.ts +++ b/packages/kit/types/ambient.d.ts @@ -253,7 +253,13 @@ declare module '@sveltejs/kit/hooks' { * /** @type {import('@sveltejs/kit').Handle} *\/ * async function first({ event, resolve }) { * console.log('first pre-processing'); - * const result = await resolve(event); + * const result = await resolve(event, { + * transformPageChunk: ({ html }) => { + * // transforms are applied in reverse order + * console.log('first transform'); + * return html; + * } + * }); * console.log('first post-processing'); * return result; * } @@ -261,7 +267,12 @@ declare module '@sveltejs/kit/hooks' { * /** @type {import('@sveltejs/kit').Handle} *\/ * async function second({ event, resolve }) { * console.log('second pre-processing'); - * const result = await resolve(event); + * const result = await resolve(event, { + * transformPageChunk: ({ html }) => { + * console.log('second transform'); + * return html; + * } + * }); * console.log('second post-processing'); * return result; * } @@ -274,6 +285,8 @@ declare module '@sveltejs/kit/hooks' { * ``` * first pre-processing * second pre-processing + * second transform + * first transform * second post-processing * first post-processing * ```