kit icon indicating copy to clipboard operation
kit copied to clipboard

Consider how to pass options between handlers in sequence

Open f-elix opened this issue 3 years ago • 2 comments

Describe the bug

When using the sequence helper to chain multiple handle functions, transformPage is only actually run in the last one. Not sure if it was intended or not, but it would be nice if we could use transformPage in all handles, regardless of order.

Reproduction

https://stackblitz.com/edit/sveltejs-kit-template-default-wg5zme?file=src%2Fhooks.js&terminal=dev

You can see in the reproduction that the html lang attribute is not changed. If we swap the two handle functions so that the one that uses transformPage comes last, the html is changed.

Logs

No response

System Info

System:
    OS: Windows 10 10.0.22000
    CPU: (8) x64 Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
    Memory: 19.88 GB / 39.76 GB
  Binaries:
    Node: 16.13.1 - C:\Program Files\nodejs\node.EXE
    npm: 8.4.1 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Spartan (44.22000.120.0), Chromium (98.0.1108.43)
    Internet Explorer: 11.0.22000.120
  npmPackages:
    @sveltejs/adapter-vercel: 1.0.0-next.43 => 1.0.0-next.43
    @sveltejs/kit: ^1.0.0-next.278 => 1.0.0-next.278
    svelte: ^3.46.4 => 3.46.4

Severity

serious, but I can work around it

Additional Information

No response

f-elix avatar Feb 22 '22 14:02 f-elix

Yeah, this is a broader issue around passing options between handlers. I think we probably want to change the behaviour of sequence to something like this...

/**
 * @param {...import('types').Handle} handlers
 * @returns {import('types').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('types').RequestEvent} event
     * @returns {import('types').MaybePromise<Response>}
     */
-    function apply_handle(i, event) {
+    function apply_handle(i, event, options = {}) {
      const handle = handlers[i];

      return handle({
        event,
-        resolve: i < length - 1 ? (event) => apply_handle(i + 1, event) : resolve
+        resolve: i < length - 1 ? (event, options) => apply_handle(i + 1, event, options) : resolve,
+        options
      });
    }
  };
}

...which would change the API to this:

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);
+  const result = await resolve(event, { transformPage });
  console.log('first post-processing');
  return result;
}
 
/** @type {import('@sveltejs/kit').Handle} */
-async function second({ event, resolve }) {
+async function second({ event, resolve, options }) {
  console.log('second pre-processing');
-  const result = await resolve(event);
+  const result = await resolve(event, options);
  console.log('second post-processing');
  return result;
}
 
export const handle = sequence(first, second);

Another idea we discussed in the maintainers' chat recently was to automatically merge options on the way 'down', but I think I prefer the explicit approach.

Rich-Harris avatar Feb 22 '22 17:02 Rich-Harris

Thanks for the explanation!

My 2 cents:

It seems to me that having to explicitly pass the options to every other handles would become cumbersome if you have a lot of them. Moreover, I also don't think automatically merging the options would remove any flexibility for the user.

f-elix avatar Feb 22 '22 19:02 f-elix