From f16f4d1da80f3ac279a9c25cc08729028f5bec2c Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 27 Nov 2024 12:27:21 +0100 Subject: [PATCH] feat: support server-rendering attributes of `` blocks Companion to https://github.com/sveltejs/svelte/pull/14397 --- .changeset/tricky-garlics-mate.md | 5 +++++ .../10-getting-started/30-project-structure.md | 1 + packages/kit/src/core/sync/write_server.js | 4 +++- packages/kit/src/runtime/server/page/render.js | 15 ++++++++++++++- packages/kit/src/types/internal.d.ts | 2 ++ playgrounds/basic/src/app.html | 2 +- 6 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 .changeset/tricky-garlics-mate.md diff --git a/.changeset/tricky-garlics-mate.md b/.changeset/tricky-garlics-mate.md new file mode 100644 index 000000000000..976f7a80f424 --- /dev/null +++ b/.changeset/tricky-garlics-mate.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: support server-rendering attributes of `` blocks diff --git a/documentation/docs/10-getting-started/30-project-structure.md b/documentation/docs/10-getting-started/30-project-structure.md index 34b938c0a862..9d1630d8e898 100644 --- a/documentation/docs/10-getting-started/30-project-structure.md +++ b/documentation/docs/10-getting-started/30-project-structure.md @@ -48,6 +48,7 @@ The `src` directory contains the meat of your project. Everything except `src/ro - `%sveltekit.assets%` — either [`paths.assets`](configuration#paths), if specified, or a relative path to [`paths.base`](configuration#paths) - `%sveltekit.nonce%` — a [CSP](configuration#csp) nonce for manually included links and scripts, if used - `%sveltekit.env.[NAME]%` - this will be replaced at render time with the `[NAME]` environment variable, which must begin with the [`publicPrefix`](configuration#env) (usually `PUBLIC_`). It will fallback to `''` if not matched. + - `%svelte.htmlAttributes%` — the attributes collected from `` blocks - `error.html` is the page that is rendered when everything else fails. It can contain the following placeholders: - `%sveltekit.status%` — the HTTP status - `%sveltekit.error.message%` — the error message diff --git a/packages/kit/src/core/sync/write_server.js b/packages/kit/src/core/sync/write_server.js index eb50bfd4735b..c479aef07c81 100644 --- a/packages/kit/src/core/sync/write_server.js +++ b/packages/kit/src/core/sync/write_server.js @@ -37,6 +37,7 @@ import { set_private_env, set_public_env, set_safe_public_env } from '${runtime_ export const options = { app_dir: ${s(config.kit.appDir)}, app_template_contains_nonce: ${template.includes('%sveltekit.nonce%')}, + app_template_contains_svelte_htmlAttributes: ${template.includes('%svelte.htmlAttributes%')}, csp: ${s(config.kit.csp)}, csrf_check_origin: ${s(config.kit.csrf.checkOrigin)}, embedded: ${config.kit.embedded}, @@ -47,7 +48,8 @@ export const options = { root, service_worker: ${has_service_worker}, templates: { - app: ({ head, body, assets, nonce, env }) => ${s(template) + app: ({ html_attributes, head, body, assets, nonce, env }) => ${s(template) + .replace('%svelte.htmlAttributes%', '" + html_attributes + "') .replace('%sveltekit.head%', '" + head + "') .replace('%sveltekit.body%', '" + body + "') .replace(/%sveltekit\.assets%/g, '" + assets + "') diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index be44c3c7a860..c6d962924b01 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -191,7 +191,17 @@ export async function render_response({ } } } else { - rendered = { head: '', html: '', css: { code: '', map: null } }; + rendered = { head: '', html: '', css: { code: '', map: null }, htmlAttributes: '' }; + } + + if ( + !options.app_template_contains_svelte_htmlAttributes && + // @ts-expect-error only exists in later versions of Svelte 5 + rendered.htmlAttributes + ) { + console.warn( + 'One or more components used `` to output attributes to the HTML tag but app.html does not contain %svelte.htmlAttributes% to render them. The attributes will be ignored.' + ); } let head = ''; @@ -458,6 +468,9 @@ export async function render_response({ head += rendered.head; const html = options.templates.app({ + // @ts-expect-error only exists in later versions of Svelte 5 + html_attributes: rendered.htmlAttributes ?? '', + head, body, assets, diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index c9dbb51ce007..8ba73dc190fc 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -355,6 +355,7 @@ export type SSRNodeLoader = () => Promise; export interface SSROptions { app_dir: string; app_template_contains_nonce: boolean; + app_template_contains_svelte_htmlAttributes: boolean; csp: ValidatedConfig['kit']['csp']; csrf_check_origin: boolean; embedded: boolean; @@ -366,6 +367,7 @@ export interface SSROptions { service_worker: boolean; templates: { app(values: { + htmlAttributes: string; head: string; body: string; assets: string; diff --git a/playgrounds/basic/src/app.html b/playgrounds/basic/src/app.html index 77a5ff52c923..f7e2cf5e9703 100644 --- a/playgrounds/basic/src/app.html +++ b/playgrounds/basic/src/app.html @@ -1,5 +1,5 @@ - +