Repository: sveltejs/kit
Pull request #8010 (2022-12-09 00:51:43)

add width/height attributes
another lighthouse rec

changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: c6bc65af55cdedd1ce359b080d5bccb1dd1b3d54

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/sveltejs/kit/new/image-dimensions?filename=.changeset/strange-terms-agree.md&value=---%0A%22kit.svelte.dev%22%3A%20patch%0A---%0A%0Aadd%20width%2Fheight%20attributes%0A)



Patch:
diff --git a/.changeset/popular-crabs-shop.md b/.changeset/popular-crabs-shop.md
new file mode 100644
index 000000000..452168d18
--- /dev/null
+++ b/.changeset/popular-crabs-shop.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[breaking] add embedded option, turned off by default
diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js
index f6da4aec9..12af12bf6 100644
--- a/packages/kit/src/core/config/index.spec.js
+++ b/packages/kit/src/core/config/index.spec.js
@@ -76,6 +76,7 @@ const get_defaults = (prefix = '') => ({
 			checkOrigin: true
 		},
 		endpointExtensions: undefined,
+		embedded: false,
 		env: {
 			dir: process.cwd(),
 			publicPrefix: 'PUBLIC_'
diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js
index 29ec6d3d7..4d3aa71cc 100644
--- a/packages/kit/src/core/config/options.js
+++ b/packages/kit/src/core/config/options.js
@@ -129,6 +129,8 @@ const options = object(
 				checkOrigin: boolean(true)
 			}),
 
+			embedded: boolean(false),
+
 			// TODO: remove this for the 1.0 release
 			endpointExtensions: error(
 				(keypath) => `${keypath} has been renamed to config.kit.moduleExtensions`
diff --git a/packages/kit/src/exports/vite/build/build_server.js b/packages/kit/src/exports/vite/build/build_server.js
index 6ecd90272..a0c73bc1e 100644
--- a/packages/kit/src/exports/vite/build/build_server.js
+++ b/packages/kit/src/exports/vite/build/build_server.js
@@ -66,6 +66,7 @@ export class Server {
 				check_origin: ${s(config.kit.csrf.checkOrigin)},
 			},
 			dev: false,
+			embedded: ${config.kit.embedded},
 			handle_error: (error, event) => {
 				return this.options.hooks.handleError({
 					error,
diff --git a/packages/kit/src/exports/vite/build/utils.js b/packages/kit/src/exports/vite/build/utils.js
index 1dfc497a3..00877dc38 100644
--- a/packages/kit/src/exports/vite/build/utils.js
+++ b/packages/kit/src/exports/vite/build/utils.js
@@ -147,7 +147,8 @@ export function get_default_build_config({ config, input, ssr, outDir }) {
 			__SVELTEKIT_APP_VERSION_FILE__: JSON.stringify(`${config.kit.appDir}/version.json`),
 			__SVELTEKIT_APP_VERSION_POLL_INTERVAL__: JSON.stringify(config.kit.version.pollInterval),
 			__SVELTEKIT_BROWSER__: ssr ? 'false' : 'true',
-			__SVELTEKIT_DEV__: 'false'
+			__SVELTEKIT_DEV__: 'false',
+			__SVELTEKIT_EMBEDDED__: config.kit.embedded ? 'true' : 'false'
 		},
 		publicDir: ssr ? false : config.kit.files.assets,
 		resolve: {
diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js
index e751c0b07..37f574139 100644
--- a/packages/kit/src/exports/vite/dev/index.js
+++ b/packages/kit/src/exports/vite/dev/index.js
@@ -449,6 +449,7 @@ export async function dev(vite, vite_config, svelte_config) {
 							check_origin: svelte_config.kit.csrf.checkOrigin
 						},
 						dev: true,
+						embedded: svelte_config.kit.embedded,
 						handle_error: async (error, event) => {
 							const error_object = await hooks.handleError({
 								error: new Proxy(error, {
diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js
index 70cae19b5..1e1fd09c9 100644
--- a/packages/kit/src/exports/vite/index.js
+++ b/packages/kit/src/exports/vite/index.js
@@ -262,7 +262,8 @@ function kit({ svelte_config }) {
 				define: {
 					__SVELTEKIT_APP_VERSION_POLL_INTERVAL__: '0',
 					__SVELTEKIT_BROWSER__: config_env.ssrBuild ? 'false' : 'true',
-					__SVELTEKIT_DEV__: 'true'
+					__SVELTEKIT_DEV__: 'true',
+					__SVELTEKIT_EMBEDDED__: svelte_config.kit.embedded ? 'true' : 'false'
 				},
 				publicDir: svelte_config.kit.files.assets,
 				resolve: {
diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js
index 70035c426..e9654a7de 100644
--- a/packages/kit/src/runtime/client/client.js
+++ b/packages/kit/src/runtime/client/client.js
@@ -89,6 +89,7 @@ function check_for_removed_attributes() {
  * @returns {import('./types').Client}
  */
 export function create_client({ target, base }) {
+	const container = __SVELTEKIT_EMBEDDED__ ? target : document.documentElement;
 	/** @type {Array<((url: URL) => boolean)>} */
 	const invalidated = [];
 
@@ -1199,7 +1200,7 @@ export function create_client({ target, base }) {
 		/** @type {NodeJS.Timeout} */
 		let mousemove_timeout;
 
-		target.addEventListener('mousemove', (event) => {
+		container.addEventListener('mousemove', (event) => {
 			const target = /** @type {Element} */ (event.target);
 
 			clearTimeout(mousemove_timeout);
@@ -1213,8 +1214,8 @@ export function create_client({ target, base }) {
 			preload(/** @type {Element} */ (event.composedPath()[0]), 1);
 		}
 
-		target.addEventListener('mousedown', tap);
-		target.addEventListener('touchstart', tap, { passive: true });
+		container.addEventListener('mousedown', tap);
+		container.addEventListener('touchstart', tap, { passive: true });
 
 		const observer = new IntersectionObserver(
 			(entries) => {
@@ -1233,7 +1234,7 @@ export function create_client({ target, base }) {
 		 * @param {number} priority
 		 */
 		function preload(element, priority) {
-			const a = find_anchor(element, target);
+			const a = find_anchor(element, container);
 			if (!a) return;
 
 			const { url, external } = get_link_info(a, base);
@@ -1253,7 +1254,7 @@ export function create_client({ target, base }) {
 		function after_navigate() {
 			observer.disconnect();
 
-			for (const a of target.querySelectorAll('a')) {
+			for (const a of container.querySelectorAll('a')) {
 				const { url, external } = get_link_info(a, base);
 				if (external) continue;
 
@@ -1457,14 +1458,14 @@ export function create_client({ target, base }) {
 			}
 
 			/** @param {MouseEvent} event */
-			target.addEventListener('click', (event) => {
+			container.addEventListener('click', (event) => {
 				// Adapted from https://github.com/visionmedia/page.js
 				// MIT license https://github.com/visionmedia/page.js#license
 				if (event.button || event.which !== 1) return;
 				if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
 				if (event.defaultPrevented) return;
 
-				const a = find_anchor(/** @type {Element} */ (event.composedPath()[0]), target);
+				const a = find_anchor(/** @type {Element} */ (event.composedPath()[0]), container);
 				if (!a) return;
 
 				const { url, external, has } = get_link_info(a, base);
@@ -1534,7 +1535,7 @@ export function create_client({ target, base }) {
 				});
 			});
 
-			target.addEventListener('submit', (event) => {
+			container.addEventListener('submit', (event) => {
 				if (event.defaultPrevented) return;
 
 				const form = /** @type {HTMLFormElement} */ (
@@ -1639,11 +1640,25 @@ export function create_client({ target, base }) {
 			});
 		},
 
-		_hydrate: async ({ status, error, node_ids, params, route, data: server_data_nodes, form }) => {
+		_hydrate: async ({
+			status = 200,
+			error,
+			node_ids,
+			params,
+			route,
+			data: server_data_nodes,
+			form
+		}) => {
 			hydrated = true;
 
 			const url = new URL(location.href);
 
+			if (!__SVELTEKIT_EMBEDDED__) {
+				// See https://github.com/sveltejs/kit/pull/4935#issuecomment-1328093358 for one motivation
+				// of determining the params on the client side.
+				({ params = {}, route = { id: null } } = get_navigation_intent(url, false) || {});
+			}
+
 			/** @type {import('./types').NavigationFinished | undefined} */
 			let result;
 
diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js
index 18c6dd807..5d1680e99 100644
--- a/packages/kit/src/runtime/server/page/render.js
+++ b/packages/kit/src/runtime/server/page/render.js
@@ -266,24 +266,41 @@ export async function render_response({
 	}
 
 	if (page_config.csr) {
+		const opts = [
+			`env: ${s(options.public_env)}`,
+			`paths: ${s(options.paths)}`,
+			`target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode`,
+			`version: ${s(options.version)}`
+		];
+
+		if (page_config.ssr) {
+			const hydrate = [
+				`node_ids: [${branch.map(({ node }) => node.index).join(', ')}]`,
+				`data: ${serialized.data}`,
+				`form: ${serialized.form}`
+			];
+
+			if (status !== 200) {
+				hydrate.push(`status: ${status}`);
+			}
+
+			if (error) {
+				hydrate.push(`error: ${devalue.uneval(error)}`);
+			}
+
+			if (options.embedded) {
+				hydrate.push(`params: ${devalue.uneval(event.params)}`, `route: ${s(event.route)}`);
+			}
+
+			opts.push(`hydrate: {\n\t\t\t\t\t${hydrate.join(',\n\t\t\t\t\t')}\n\t\t\t\t}`);
+		}
+
 		// prettier-ignore
 		const init_app = `
 			import { start } from ${s(prefixed(entry.file))};
 
 			start({
-				env: ${s(options.public_env)},
-				hydrate: ${page_config.ssr ? `{
-					status: ${status},
-					error: ${devalue.uneval(error)},
-					node_ids: [${branch.map(({ node }) => node.index).join(', ')}],
-					params: ${devalue.uneval(event.params)},
-					route: ${s(event.route)},
-					data: ${serialized.data},
-					form: ${serialized.form}
-				}` : 'null'},
-				paths: ${s(options.paths)},
-				target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode,
-				version: ${s(options.version)}
+				${opts.join(',\n\t\t\t\t')}
 			});
 		`;
 
diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts
index 4a83dd195..5572d3475 100644
--- a/packages/kit/types/index.d.ts
+++ b/packages/kit/types/index.d.ts
@@ -325,6 +325,11 @@ export interface KitConfig {
 		 */
 		checkOrigin?: boolean;
 	};
+	/**
+	 * Whether or not the app is embedded inside a larger app. If `true`, SvelteKit will add its event listeners related to navigation etc on the parent of `%sveltekit.body%` instead of `window`, and will pass `params` from the server rather than inferring them from `location.pathname`.
+	 * @default false
+	 */
+	embedded?: boolean;
 	/**
 	 * Environment variable configuration
 	 */
diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts
index 2bc94ac73..526bf8f9d 100644
--- a/packages/kit/types/internal.d.ts
+++ b/packages/kit/types/internal.d.ts
@@ -299,6 +299,7 @@ export interface SSROptions {
 		check_origin: boolean;
 	};
 	dev: boolean;
+	embedded: boolean;
 	handle_error(error: Error & { frame?: string }, event: RequestEvent): MaybePromise<App.Error>;
 	hooks: ServerHooks;
 	manifest: SSRManifest;
@@ -387,4 +388,5 @@ declare global {
 	const __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: number;
 	const __SVELTEKIT_BROWSER__: boolean;
 	const __SVELTEKIT_DEV__: boolean;
+	const __SVELTEKIT_EMBEDDED__: boolean;
 }
diff --git a/sites/kit.svelte.dev/src/routes/home/Deployment.svelte b/sites/kit.svelte.dev/src/routes/home/Deployment.svelte
index fde34cdb4..7f2a2757b 100644
--- a/sites/kit.svelte.dev/src/routes/home/Deployment.svelte
+++ b/sites/kit.svelte.dev/src/routes/home/Deployment.svelte
@@ -112,6 +112,8 @@
 		<div class="globe">
 			<img
 				src="/edge.svg"
+				width="1000"
+				height="1000"
 				alt="Dynamically rendered map of the world, centered on the user's location"
 			/>
 			<span> rendered on the edge, just for you </span>
@@ -121,15 +123,17 @@
 
 <style>
 	.globe {
+		--max-size: 40rem;
 		position: relative;
 		width: 100%;
-		max-height: 40rem;
+		max-height: var(--max-size);
 		text-align: center;
 		aspect-ratio: 1;
 	}
 
 	.globe img {
-		max-width: 40rem;
+		max-width: var(--max-size);
+		max-height: var(--max-size);
 	}
 
 	.globe span {


Test patch:
diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js
index b81213171..0d0395b2d 100644
--- a/packages/kit/test/apps/basics/test/client.test.js
+++ b/packages/kit/test/apps/basics/test/client.test.js
@@ -809,11 +809,11 @@ test.describe('Routing', () => {
 		expect(await page.textContent('h1')).toBe('hello');
 	});
 
-	test('ignores clicks outside the app target', async ({ page }) => {
+	test('recognizes clicks outside the app target', async ({ page }) => {
 		await page.goto('/routing/link-outside-app-target/source');
 
 		await page.click('[href="/routing/link-outside-app-target/target"]');
-		expect(await page.textContent('h1')).toBe('target: 0');
+		await expect(page.locator('h1')).toHaveText('target: 1');
 	});
 
 	test('responds to <form method="GET"> submission without reload', async ({ page }) => {
diff --git a/packages/kit/test/apps/options/source/pages/routing/link-outside-app-target/source/+page.svelte b/packages/kit/test/apps/options/source/pages/routing/link-outside-app-target/source/+page.svelte
new file mode 100644
index 000000000..8324f9b0f
--- /dev/null
+++ b/packages/kit/test/apps/options/source/pages/routing/link-outside-app-target/source/+page.svelte
@@ -0,0 +1,8 @@
+<script>
+	import { onMount } from 'svelte';
+	import { increment, count } from '../state.js';
+
+	onMount(increment);
+</script>
+
+<h2>source: {count}</h2>
diff --git a/packages/kit/test/apps/options/source/pages/routing/link-outside-app-target/state.js b/packages/kit/test/apps/options/source/pages/routing/link-outside-app-target/state.js
new file mode 100644
index 000000000..ab2ad88ec
--- /dev/null
+++ b/packages/kit/test/apps/options/source/pages/routing/link-outside-app-target/state.js
@@ -0,0 +1,5 @@
+export let count = 0;
+
+export function increment() {
+	count += 1;
+}
diff --git a/packages/kit/test/apps/options/source/pages/routing/link-outside-app-target/target/+page.svelte b/packages/kit/test/apps/options/source/pages/routing/link-outside-app-target/target/+page.svelte
new file mode 100644
index 000000000..b3b5da849
--- /dev/null
+++ b/packages/kit/test/apps/options/source/pages/routing/link-outside-app-target/target/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+	import { count } from '../state.js';
+</script>
+
+<h2>target: {count}</h2>
diff --git a/packages/kit/test/apps/options/source/template.html b/packages/kit/test/apps/options/source/template.html
index a60716011..f88f7128c 100644
--- a/packages/kit/test/apps/options/source/template.html
+++ b/packages/kit/test/apps/options/source/template.html
@@ -8,5 +8,6 @@
 	<body>
 		<h1>I am in the template</h1>
 		<div>%sveltekit.body%</div>
+		<a href="/path-base/routing/link-outside-app-target/target">outside app target</a>
 	</body>
 </html>
diff --git a/packages/kit/test/apps/options/svelte.config.js b/packages/kit/test/apps/options/svelte.config.js
index bf17940ba..bae1cda49 100644
--- a/packages/kit/test/apps/options/svelte.config.js
+++ b/packages/kit/test/apps/options/svelte.config.js
@@ -2,6 +2,7 @@
 const config = {
 	extensions: ['.jesuslivesineveryone', '.whokilledthemuffinman', '.svelte.md', '.svelte'],
 	kit: {
+		embedded: true,
 		csp: {
 			directives: {
 				'script-src': ['self']
diff --git a/packages/kit/test/apps/options/test/test.js b/packages/kit/test/apps/options/test/test.js
index a60fdcb55..5904672d0 100644
--- a/packages/kit/test/apps/options/test/test.js
+++ b/packages/kit/test/apps/options/test/test.js
@@ -229,3 +229,12 @@ test.describe('Vite options', () => {
 		expect(await page.textContent('h2')).toBe(`${mode} === ${mode} === ${mode}`);
 	});
 });
+
+test.describe('Routing', () => {
+	test('ignores clicks outside the app target', async ({ page }) => {
+		await page.goto('/path-base/routing/link-outside-app-target/source');
+
+		await page.click('[href="/path-base/routing/link-outside-app-target/target"]');
+		await expect(page.locator('h2')).toHaveText('target: 0');
+	});
+});