From cb3c4c73e3717aef915c28ed3a9e4fc967bcd6a7 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 21 Jul 2022 12:38:29 -0400 Subject: [PATCH 1/5] replace transformPage with transformPageChunk - closes #4910 --- .changeset/nasty-seahorses-know.md | 5 +++ documentation/docs/06-hooks.md | 4 +-- documentation/docs/17-seo.md | 8 +++-- packages/kit/src/runtime/server/index.js | 12 +++++-- .../kit/src/runtime/server/page/render.js | 6 ++-- packages/kit/test/apps/amp/src/hooks.js | 32 +++++++++++-------- packages/kit/test/apps/basics/src/hooks.js | 2 +- .../index.svelte | 0 packages/kit/test/apps/basics/test/test.js | 4 +-- .../kit/test/prerendering/basics/src/hooks.js | 7 ++-- packages/kit/types/index.d.ts | 2 +- 11 files changed, 54 insertions(+), 28 deletions(-) create mode 100644 .changeset/nasty-seahorses-know.md rename packages/kit/test/apps/basics/src/routes/{transform-page => transform-page-chunk}/index.svelte (100%) diff --git a/.changeset/nasty-seahorses-know.md b/.changeset/nasty-seahorses-know.md new file mode 100644 index 000000000000..5a59f9633b44 --- /dev/null +++ b/.changeset/nasty-seahorses-know.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[breaking] replace transformPage with transformPageChunk diff --git a/documentation/docs/06-hooks.md b/documentation/docs/06-hooks.md index 31c9e583c0a1..d6a4bbfbeee6 100644 --- a/documentation/docs/06-hooks.md +++ b/documentation/docs/06-hooks.md @@ -67,7 +67,7 @@ You can add call multiple `handle` functions with [the `sequence` helper functio `resolve` also supports a second, optional parameter that gives you more control over how the response will be rendered. That parameter is an object that can have the following fields: - `ssr: boolean` (default `true`) — if `false`, renders an empty 'shell' page instead of server-side rendering -- `transformPage(opts: { html: string }): string` — applies custom transforms to HTML +- `transformPageChunk(opts: { html: string, done: boolean }): string` — applies custom transforms to HTML. If `done` is true, it's the final chunk ```js /// file: src/hooks.js @@ -75,7 +75,7 @@ You can add call multiple `handle` functions with [the `sequence` helper functio export async function handle({ event, resolve }) { const response = await resolve(event, { ssr: !event.url.pathname.startsWith('/admin'), - transformPage: ({ html }) => html.replace('old', 'new') + transformPageChunk: ({ html }) => html.replace('old', 'new') }); return response; diff --git a/documentation/docs/17-seo.md b/documentation/docs/17-seo.md index 4eb0b4e8e627..5e807a09c425 100644 --- a/documentation/docs/17-seo.md +++ b/documentation/docs/17-seo.md @@ -105,15 +105,19 @@ const config = { export default config; ``` -...and transforming the HTML using `transformPage` along with `transform` imported from `@sveltejs/amp`: +...and transforming the HTML using `transformPageChunk` along with `transform` imported from `@sveltejs/amp`: ```js import * as amp from '@sveltejs/amp'; /** @type {import('@sveltejs/kit').Handle} */ export async function handle({ event, resolve }) { + let buffer = ''; return resolve(event, { - transformPage: ({ html }) => amp.transform(html) + transformPageChunk: ({ html, done }) => { + buffer += html; + if (done) return amp.transform(html); + } }); } ``` diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 9a77c95dc479..53f3365500f3 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -171,7 +171,7 @@ export async function respond(request, options, state) { /** @type {import('types').RequiredResolveOptions} */ let resolve_opts = { ssr: true, - transformPage: default_transform + transformPageChunk: default_transform }; // TODO match route before calling handle? @@ -181,9 +181,17 @@ export async function respond(request, options, state) { event, resolve: async (event, opts) => { if (opts) { + // TODO remove for 1.0 + // @ts-expect-error + if (opts.transformPage) { + throw new Error( + 'transformPage has been replaced by transformPageChunk — see https://github.com/sveltejs/kit/issues/4910 for more information' + ); + } + resolve_opts = { ssr: opts.ssr !== false, - transformPage: opts.transformPage || default_transform + transformPageChunk: opts.transformPageChunk || default_transform }; } diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index fb6bc1bb4eac..2bd01be94c5c 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -291,8 +291,10 @@ export async function render_response({ const assets = options.paths.assets || (segments.length > 0 ? segments.map(() => '..').join('/') : '.'); - const html = await resolve_opts.transformPage({ - html: options.template({ head, body, assets, nonce: /** @type {string} */ (csp.nonce) }) + // TODO flush chunks as early as we can + const html = await resolve_opts.transformPageChunk({ + html: options.template({ head, body, assets, nonce: /** @type {string} */ (csp.nonce) }), + done: true }); const headers = new Headers({ diff --git a/packages/kit/test/apps/amp/src/hooks.js b/packages/kit/test/apps/amp/src/hooks.js index c75f6f29116a..ab885588423b 100644 --- a/packages/kit/test/apps/amp/src/hooks.js +++ b/packages/kit/test/apps/amp/src/hooks.js @@ -3,22 +3,28 @@ import * as amp from '@sveltejs/amp'; /** @type {import('@sveltejs/kit').Handle} */ export async function handle({ event, resolve }) { + let buffer = ''; + const response = await resolve(event, { - transformPage: ({ html }) => { - html = amp.transform(html); + transformPageChunk: ({ html, done }) => { + buffer += html; + + if (done) { + const html = amp.transform(buffer); - // remove unused CSS - let css = ''; - const markup = html.replace( - /`; - } - ); + // remove unused CSS + let css = ''; + const markup = html.replace( + /`; + } + ); - css = purify(markup, css); - return markup.replace('', `${css}`); + css = purify(markup, css); + return markup.replace('', `${css}`); + } } }); diff --git a/packages/kit/test/apps/basics/src/hooks.js b/packages/kit/test/apps/basics/src/hooks.js index 2af673b90932..9189f0b3acd4 100644 --- a/packages/kit/test/apps/basics/src/hooks.js +++ b/packages/kit/test/apps/basics/src/hooks.js @@ -47,7 +47,7 @@ export const handle = sequence( const response = await resolve(event, { ssr: !event.url.pathname.startsWith('/no-ssr'), - transformPage: event.url.pathname.startsWith('/transform-page') + transformPageChunk: event.url.pathname.startsWith('/transform-page-chunk') ? ({ html }) => html.replace('__REPLACEME__', 'Worked!') : undefined }); diff --git a/packages/kit/test/apps/basics/src/routes/transform-page/index.svelte b/packages/kit/test/apps/basics/src/routes/transform-page-chunk/index.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/transform-page/index.svelte rename to packages/kit/test/apps/basics/src/routes/transform-page-chunk/index.svelte diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index a2fbed968578..4de2e65a0a28 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1009,8 +1009,8 @@ test.describe('Page options', () => { } }); - test('transformPage can change the html output', async ({ page }) => { - await page.goto('/transform-page'); + test('transformPageChunk can change the html output', async ({ page }) => { + await page.goto('/transform-page-chunk'); expect(await page.getAttribute('meta[name="transform-page"]', 'content')).toBe('Worked!'); }); diff --git a/packages/kit/test/prerendering/basics/src/hooks.js b/packages/kit/test/prerendering/basics/src/hooks.js index 9e353b692da1..b8df4f86b0a8 100644 --- a/packages/kit/test/prerendering/basics/src/hooks.js +++ b/packages/kit/test/prerendering/basics/src/hooks.js @@ -2,13 +2,14 @@ import { prerendering } from '$app/env'; const initial_prerendering = prerendering; +/** @type {import('@sveltejs/kit').Handle} */ export const handle = async ({ event, resolve }) => { if (event.url.pathname === '/prerendering-true' && prerendering) { return await resolve(event, { - transformPage: ({ html }) => + transformPageChunk: ({ html }) => html - .replace('__INITIAL_PRERENDERING__', initial_prerendering) - .replace('__PRERENDERING__', prerendering) + .replace('__INITIAL_PRERENDERING__', String(initial_prerendering)) + .replace('__PRERENDERING__', String(prerendering)) }); } return await resolve(event); diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index cb9f9fc16376..6d99c45a3fdc 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -261,7 +261,7 @@ export interface RequestHandlerOutput { export interface ResolveOptions { ssr?: boolean; - transformPage?: ({ html }: { html: string }) => MaybePromise; + transformPageChunk?: (input: { html: string; done: boolean }) => MaybePromise; } export type ResponseBody = JSONValue | Uint8Array | ReadableStream | Error; From c2d845a35742312318eb91eeabe017fa27954e41 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 21 Jul 2022 12:47:50 -0400 Subject: [PATCH 2/5] Update packages/kit/src/runtime/server/index.js --- packages/kit/src/runtime/server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 53f3365500f3..04406081ea58 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -185,7 +185,7 @@ export async function respond(request, options, state) { // @ts-expect-error if (opts.transformPage) { throw new Error( - 'transformPage has been replaced by transformPageChunk — see https://github.com/sveltejs/kit/issues/4910 for more information' + 'transformPage has been replaced by transformPageChunk — see https://github.com/sveltejs/kit/pull/5657 for more information' ); } From 28f13bcc40729068254fdd77faf772f37866e9e6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 21 Jul 2022 12:52:37 -0400 Subject: [PATCH 3/5] fix type --- packages/kit/types/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 6d99c45a3fdc..dbf012c41eb3 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -261,7 +261,7 @@ export interface RequestHandlerOutput { export interface ResolveOptions { ssr?: boolean; - transformPageChunk?: (input: { html: string; done: boolean }) => MaybePromise; + transformPageChunk?: (input: { html: string; done: boolean }) => MaybePromise; } export type ResponseBody = JSONValue | Uint8Array | ReadableStream | Error; From bfc86e6e944673c2046b5f5436eab3eadd83a46d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 21 Jul 2022 15:03:44 -0400 Subject: [PATCH 4/5] fix --- packages/kit/src/runtime/server/page/render.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 2bd01be94c5c..a9d38a19e6d1 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -292,10 +292,11 @@ export async function render_response({ options.paths.assets || (segments.length > 0 ? segments.map(() => '..').join('/') : '.'); // TODO flush chunks as early as we can - const html = await resolve_opts.transformPageChunk({ - html: options.template({ head, body, assets, nonce: /** @type {string} */ (csp.nonce) }), - done: true - }); + const html = + (await resolve_opts.transformPageChunk({ + html: options.template({ head, body, assets, nonce: /** @type {string} */ (csp.nonce) }), + done: true + })) || ''; const headers = new Headers({ 'content-type': 'text/html', From 193a3a4e8ec2d09a95f93242493fad13fe3e5c3c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 21 Jul 2022 18:42:48 -0400 Subject: [PATCH 5/5] update docs --- documentation/docs/06-hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/06-hooks.md b/documentation/docs/06-hooks.md index d6a4bbfbeee6..a6b28d5d28f5 100644 --- a/documentation/docs/06-hooks.md +++ b/documentation/docs/06-hooks.md @@ -67,7 +67,7 @@ You can add call multiple `handle` functions with [the `sequence` helper functio `resolve` also supports a second, optional parameter that gives you more control over how the response will be rendered. That parameter is an object that can have the following fields: - `ssr: boolean` (default `true`) — if `false`, renders an empty 'shell' page instead of server-side rendering -- `transformPageChunk(opts: { html: string, done: boolean }): string` — applies custom transforms to HTML. If `done` is true, it's the final chunk +- `transformPageChunk(opts: { html: string, done: boolean }): MaybePromise` — applies custom transforms to HTML. If `done` is true, it's the final chunk. Chunks are not guaranteed to be well-formed HTML (they could include an element's opening tag but not its closing tag, for example) but they will always be split at sensible boundaries such as `%sveltekit.head%` or layout/page components. ```js /// file: src/hooks.js