Repository: sveltejs/kit
Pull request #4994 (2022-05-19 23:02:56)

[chore] update `tsconfig.json` files in fixtures
I noticed a bunch of warnings in the CI logs

Unfortunately this doesn't seem to silence them - either with the commonly used path or the path suggested in the warning

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

Latest commit: 0e5f78f9ef618d6b81d54005c744d1d04bd97f1e

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/tsconfig-update?filename=.changeset/short-apes-leave.md&value=---%0A%22%40sveltejs%2Fkit%22%3A%20patch%0A---%0A%0A%5Bchore%5D%20update%20fixtures%20to%20silence%20warnings%0A)



Patch:
diff --git a/.changeset/big-cups-exist.md b/.changeset/big-cups-exist.md
new file mode 100644
index 000000000..4859d353d
--- /dev/null
+++ b/.changeset/big-cups-exist.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[breaking] remove amp config option in favour of amp.transform helper function
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 5ab2de403..0964535f3 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -178,6 +178,7 @@
     "eight-pets-clean",
     "eight-readers-thank",
     "eight-rivers-battle",
+    "eight-tomatoes-yawn",
     "eighty-candles-grow",
     "eighty-carrots-heal",
     "eighty-waves-obey",
@@ -884,6 +885,7 @@
     "twelve-feet-deny",
     "twelve-goats-knock",
     "twelve-onions-burn",
+    "twelve-snakes-tan",
     "twenty-dogs-shout",
     "twenty-dryers-hope",
     "twenty-hairs-shave",
@@ -969,6 +971,7 @@
     "young-ears-rhyme",
     "young-hairs-cough",
     "young-horses-kick",
+    "young-penguins-camp",
     "young-students-chew",
     "young-swans-burn",
     "young-ways-fail"
diff --git a/.changeset/shaggy-bugs-move.md b/.changeset/shaggy-bugs-move.md
new file mode 100644
index 000000000..b8fb7d15c
--- /dev/null
+++ b/.changeset/shaggy-bugs-move.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[breaking] accessing url.hash from load now results in a helpful error
diff --git a/.changeset/twelve-snakes-tan.md b/.changeset/twelve-snakes-tan.md
new file mode 100644
index 000000000..db2860288
--- /dev/null
+++ b/.changeset/twelve-snakes-tan.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Warn if load uses window.fetch
diff --git a/.changeset/young-penguins-camp.md b/.changeset/young-penguins-camp.md
new file mode 100644
index 000000000..255299069
--- /dev/null
+++ b/.changeset/young-penguins-camp.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Only run `viteServeStaticMiddleware` after server has attempted response
diff --git a/documentation/docs/02-routing.md b/documentation/docs/02-routing.md
index 88d15a051..1b2dbdb2f 100644
--- a/documentation/docs/02-routing.md
+++ b/documentation/docs/02-routing.md
@@ -62,7 +62,7 @@ declare module '$lib/database' {
 	export const get: (id: string) => Promise<Item>;
 }
 
-// @filename: [id].d.ts
+// @filename: __types/[id].d.ts
 import type { RequestHandler as GenericRequestHandler } from '@sveltejs/kit';
 export type RequestHandler<Body = any> = GenericRequestHandler<{ id: string }, Body>;
 
@@ -70,7 +70,7 @@ export type RequestHandler<Body = any> = GenericRequestHandler<{ id: string }, B
 // ---cut---
 import db from '$lib/database';
 
-/** @type {import('./[id]').RequestHandler} */
+/** @type {import('./__types/[id]').RequestHandler} */
 export async function get({ params }) {
 	// `params.id` comes from [id].js
 	const item = await db.get(params.id);
@@ -158,7 +158,7 @@ declare module '$lib/database' {
 	export const create: (request: Request) => Promise<[Record<string, ValidationError>, Item]>;
 }
 
-// @filename: items.d.ts
+// @filename: __types/items.d.ts
 import type { RequestHandler as GenericRequestHandler } from '@sveltejs/kit';
 export type RequestHandler<Body = any> = GenericRequestHandler<{}, Body>;
 
@@ -166,7 +166,7 @@ export type RequestHandler<Body = any> = GenericRequestHandler<{}, Body>;
 // ---cut---
 import * as db from '$lib/database';
 
-/** @type {import('./items').RequestHandler} */
+/** @type {import('./__types/items').RequestHandler} */
 export async function get() {
 	const items = await db.list();
 
@@ -175,7 +175,7 @@ export async function get() {
 	};
 }
 
-/** @type {import('./items').RequestHandler} */
+/** @type {import('./__types/items').RequestHandler} */
 export async function post({ request }) {
 	const [errors, item] = await db.create(request);
 
diff --git a/documentation/docs/03-layouts.md b/documentation/docs/03-layouts.md
index 1e33a4f46..1dcb2edd1 100644
--- a/documentation/docs/03-layouts.md
+++ b/documentation/docs/03-layouts.md
@@ -196,7 +196,7 @@ src/routes/
 ```svelte
 /// file: src/routes/marx-brothers/[...path].svelte
 <script context="module">
-	/** @type {import('./[...path]').Load} */
+	/** @type {import('./__types/[...path]').Load} */
 	export function load({ params }) {
 		return {
 			status: 404,
diff --git a/documentation/docs/04-loading.md b/documentation/docs/04-loading.md
index a4dd42dea..7517cab1e 100644
--- a/documentation/docs/04-loading.md
+++ b/documentation/docs/04-loading.md
@@ -9,7 +9,7 @@ If the data for a page comes from its endpoint, you may not need a `load` functi
 ```html
 /// file: src/routes/blog/[slug].svelte
 <script context="module">
-	/** @type {import('./[slug]').Load} */
+	/** @type {import('./__types/[slug]').Load} */
 	export async function load({ params, fetch, session, stuff }) {
 		const url = `https://cms.example.com/article/${params.slug}.json`;
 		const response = await fetch(url);
@@ -56,7 +56,7 @@ The `load` function receives an object containing eight fields — `url`, `param
 
 #### url
 
-`url` is an instance of [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL), containing properties like the `origin`, `hostname`, `pathname` and `searchParams` (which contains the parsed query string as a [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) object).
+`url` is an instance of [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL), containing properties like the `origin`, `hostname`, `pathname` and `searchParams` (which contains the parsed query string as a [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) object). `url.hash` cannot be accessed during `load`, since it is unavailable on the server.
 
 > In some environments this is derived from request headers during server-side rendering. If you're using [adapter-node](/docs/adapters#supported-environments-node-js), for example, you may need to configure the adapter in order for the URL to be correct.
 
diff --git a/documentation/docs/14-configuration.md b/documentation/docs/14-configuration.md
index a595b34b8..ac2053fcb 100644
--- a/documentation/docs/14-configuration.md
+++ b/documentation/docs/14-configuration.md
@@ -16,7 +16,6 @@ const config = {
 
 	kit: {
 		adapter: undefined,
-		amp: false,
 		appDir: '_app',
 		browser: {
 			hydrate: true,
@@ -92,10 +91,6 @@ export default config;
 
 Required when running `svelte-kit build` and determines how the output is converted for different platforms. See [Adapters](/docs/adapters).
 
-### amp
-
-Enable [AMP](/docs/seo#amp) mode.
-
 ### appDir
 
 The directory relative to `paths.assets` where the built JS and CSS (and imported assets) are served from. (The filenames therein contain content-based hashes, meaning they can be cached indefinitely). Must not start or end with `/`.
diff --git a/documentation/docs/16-seo.md b/documentation/docs/16-seo.md
index c75e61864..0e61d988e 100644
--- a/documentation/docs/16-seo.md
+++ b/documentation/docs/16-seo.md
@@ -22,7 +22,7 @@ SvelteKit redirects pathnames with trailing slashes to ones without (or vice ver
 
 ### Manual setup
 
-#### &lt;title&gt; and &lt;meta&gt; 
+#### &lt;title&gt; and &lt;meta&gt;
 
 Every page should have well-written and unique `<title>` and `<meta name="description">` elements inside a [`<svelte:head>`](https://svelte.dev/docs#template-syntax-svelte-head). Guidance on how to write descriptive titles and descriptions, along with other suggestions on making content understandable by search engines, can be found on Google's [Lighthouse SEO audits](https://web.dev/lighthouse-seo/) documentation.
 
@@ -82,8 +82,40 @@ export async function get() {
 
 #### AMP
 
-An unfortunate reality of modern web development is that it is sometimes necessary to create an [Accelerated Mobile Pages (AMP)](https://amp.dev/) version of your site. In SvelteKit this can be done by setting the [`amp`](/docs/configuration#amp) config option, which has the following effects:
+An unfortunate reality of modern web development is that it is sometimes necessary to create an [Accelerated Mobile Pages (AMP)](https://amp.dev/) version of your site. In SvelteKit this can be done by enforcing the following [configuration](/docs/configuration) options...
 
-- Client-side JavaScript, including the router, is disabled
-- Styles are concatenated into `<style amp-custom>`, and the [AMP boilerplate](https://amp.dev/boilerplate/) is injected
-- In development, requests are checked against the [AMP validator](https://validator.ampproject.org/) so you get early warning of any errors
+```js
+/// file: svelte.config.js
+/** @type {import('@sveltejs/kit').Config} */
+const config = {
+	kit: {
+		// the combination of these options
+		// disables JavaScript
+		browser: {
+			hydrate: false,
+			router: false
+		},
+
+		// since <link rel="stylesheet"> isn't
+		// allowed, inline all styles
+		inlineStyleThreshold: Infinity
+	}
+};
+
+export default config;
+```
+
+...and transforming the HTML using `transformPage` 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 }) {
+	return resolve(event, {
+		transformPage: ({ html }) => amp.transform(html)
+	});
+}
+```
+
+> It's a good idea to use the `handle` hook to validate the transformed HTML using `amphtml-validator`, but only if you're prerendering pages since it's very slow.
diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js
index 6c89940d8..fe9e3a55a 100644
--- a/packages/adapter-vercel/index.js
+++ b/packages/adapter-vercel/index.js
@@ -405,7 +405,9 @@ function get_node_version() {
 	const major = parseInt(full.split('.')[0]); // '16.5.0' --> 16
 
 	if (major < 16) {
-		throw new Error(`SvelteKit only supports Node.js version 16 or greater (currently using v${full}). Consult the documentation: https://vercel.com/docs/runtimes#official-runtimes/node-js/node-js-version`)
+		throw new Error(
+			`SvelteKit only supports Node.js version 16 or greater (currently using v${full}). Consult the documentation: https://vercel.com/docs/runtimes#official-runtimes/node-js/node-js-version`
+		);
 	}
 
 	return { major, full };
diff --git a/packages/amp/.gitignore b/packages/amp/.gitignore
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/amp/index.d.ts b/packages/amp/index.d.ts
new file mode 100644
index 000000000..475cec2b7
--- /dev/null
+++ b/packages/amp/index.d.ts
@@ -0,0 +1,1 @@
+export function transform(html: string): string;
diff --git a/packages/amp/index.js b/packages/amp/index.js
new file mode 100644
index 000000000..5b54cb891
--- /dev/null
+++ b/packages/amp/index.js
@@ -0,0 +1,28 @@
+// https://amp.dev/documentation/guides-and-tutorials/learn/spec/amp-boilerplate/
+const boilerplate = `
+	<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
+	<link rel="preload" as="script" href="https://cdn.ampproject.org/v0.js">
+	<script async src="https://cdn.ampproject.org/v0.js"></script>
+`;
+
+/** @param {string} html */
+export function transform(html) {
+	return html
+		.replace(/<style([^]+?)<\/style>/, (match, $1) => `<style amp-custom${$1}</style>`)
+		.replace(/<script[^]+?<\/script>/g, '')
+		.replace(/<link[^>]+>/g, (match) => {
+			if (/rel=('|")?stylesheet\1/.test(match)) {
+				if (/ disabled /.test(match)) return '';
+				throw new Error(
+					'An AMP document cannot contain <link rel="stylesheet"> — ensure that inlineStyleThreshold is set to Infinity, and remove links from your page template and <svelte:head> elements'
+				);
+			}
+
+			return match;
+		})
+		.replace(/<meta[^>]+>/g, (match) => {
+			if (match.includes('http-equiv')) return '';
+			return match;
+		})
+		.replace('</head>', boilerplate + '</head>');
+}
diff --git a/packages/amp/package.json b/packages/amp/package.json
new file mode 100644
index 000000000..fcc2b3086
--- /dev/null
+++ b/packages/amp/package.json
@@ -0,0 +1,28 @@
+{
+	"name": "@sveltejs/amp",
+	"version": "1.0.0-next.0",
+	"repository": {
+		"type": "git",
+		"url": "https://github.com/sveltejs/kit",
+		"directory": "packages/adapter-auto"
+	},
+	"license": "MIT",
+	"homepage": "https://kit.svelte.dev",
+	"type": "module",
+	"exports": {
+		".": {
+			"import": "./index.js"
+		},
+		"./package.json": "./package.json"
+	},
+	"main": "index.js",
+	"types": "index.d.ts",
+	"files": [
+		"index.js"
+	],
+	"scripts": {
+		"lint": "eslint --ignore-path .gitignore \"**/*.{ts,js,svelte}\" && npm run check-format",
+		"format": "npm run check-format -- --write",
+		"check-format": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore"
+	}
+}
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index f35e738c3..b03d0df47 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,15 @@
 # @sveltejs/kit
 
+## 1.0.0-next.337
+
+### Patch Changes
+
+- [fix] don't watch `outDir`, except for the `generated` directory ([#4997](https://github.com/sveltejs/kit/pull/4997))
+
+* Warn if load uses window.fetch ([#4958](https://github.com/sveltejs/kit/pull/4958))
+
+- Only run `viteServeStaticMiddleware` after server has attempted response ([#4974](https://github.com/sveltejs/kit/pull/4974))
+
 ## 1.0.0-next.336
 
 ### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index 9d21057fc..661ebd397 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.0.0-next.336",
+	"version": "1.0.0-next.337",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
@@ -13,12 +13,11 @@
 		"@sveltejs/vite-plugin-svelte": "^1.0.0-next.44",
 		"chokidar": "^3.5.3",
 		"sade": "^1.7.4",
-		"vite": "^2.9.0"
+		"vite": "^2.9.9"
 	},
 	"devDependencies": {
 		"@playwright/test": "^1.21.0",
 		"@rollup/plugin-replace": "^4.0.0",
-		"@types/amphtml-validator": "^1.0.1",
 		"@types/connect": "^3.4.35",
 		"@types/cookie": "^0.5.0",
 		"@types/marked": "^4.0.1",
@@ -26,7 +25,6 @@
 		"@types/node": "^16.11.11",
 		"@types/sade": "^1.7.3",
 		"@types/set-cookie-parser": "^2.4.2",
-		"amphtml-validator": "^1.0.35",
 		"cookie": "^0.5.0",
 		"cross-env": "^7.0.3",
 		"devalue": "^2.0.1",
diff --git a/packages/kit/src/core/build/build_client.js b/packages/kit/src/core/build/build_client.js
index cfdda0370..1e7f23bc8 100644
--- a/packages/kit/src/core/build/build_client.js
+++ b/packages/kit/src/core/build/build_client.js
@@ -31,8 +31,6 @@ export async function build_client({
 	process.env.VITE_SVELTEKIT_APP_VERSION_FILE = `${config.kit.appDir}/version.json`;
 	process.env.VITE_SVELTEKIT_APP_VERSION_POLL_INTERVAL = `${config.kit.version.pollInterval}`;
 
-	process.env.VITE_SVELTEKIT_AMP = config.kit.amp ? 'true' : '';
-
 	const client_out_dir = `${output_dir}/client/${config.kit.appDir}`;
 
 	/** @type {Record<string, string>} */
@@ -79,12 +77,7 @@ export async function build_client({
 		plugins: [
 			svelte({
 				...config,
-				// In AMP mode, we know that there are no conditional component imports. In that case, we
-				// don't need to include CSS for components that are imported but unused, so we can just
-				// include rendered CSS.
-				// This would also apply if hydrate and router are both false, but we don't know if one
-				// has been enabled at the page level, so we don't do anything there.
-				emitCss: !config.kit.amp,
+				emitCss: true,
 				compilerOptions: {
 					...config.compilerOptions,
 					hydratable: !!config.kit.browser.hydrate
diff --git a/packages/kit/src/core/build/build_server.js b/packages/kit/src/core/build/build_server.js
index 6b13537fb..d711b95d9 100644
--- a/packages/kit/src/core/build/build_server.js
+++ b/packages/kit/src/core/build/build_server.js
@@ -47,7 +47,6 @@ export function override(settings) {
 export class Server {
 	constructor(manifest) {
 		this.options = {
-			amp: ${config.kit.amp},
 			csp: ${s(config.kit.csp)},
 			dev: false,
 			floc: ${config.kit.floc},
@@ -260,7 +259,7 @@ export async function build_server(
 
 	client.assets.forEach((asset) => {
 		if (asset.fileName.endsWith('.css')) {
-			if (config.kit.amp || asset.source.length < config.kit.inlineStyleThreshold) {
+			if (asset.source.length < config.kit.inlineStyleThreshold) {
 				const index = stylesheet_lookup.size;
 				const file = `${output_dir}/server/stylesheets/${index}.js`;
 
diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js
index a7fb2b5e0..81ad6ffad 100644
--- a/packages/kit/src/core/config/index.spec.js
+++ b/packages/kit/src/core/config/index.spec.js
@@ -12,7 +12,7 @@ const get_defaults = (prefix = '') => ({
 	extensions: ['.svelte'],
 	kit: {
 		adapter: null,
-		amp: false,
+		amp: undefined,
 		appDir: '_app',
 		browser: {
 			hydrate: true,
diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js
index 47a9d221e..2286e4ab3 100644
--- a/packages/kit/src/core/config/options.js
+++ b/packages/kit/src/core/config/options.js
@@ -39,7 +39,11 @@ const options = object(
 				return input;
 			}),
 
-			amp: boolean(false),
+			// TODO: remove this for the 1.0 release
+			amp: error(
+				(keypath) =>
+					`${keypath} has been removed. See https://kit.svelte.dev/docs/seo#amp for details on how to support AMP`
+			),
 
 			appDir: validate('_app', (input, keypath) => {
 				assert_string(input, keypath);
diff --git a/packages/kit/src/core/dev/amp_hook.js b/packages/kit/src/core/dev/amp_hook.js
deleted file mode 100644
index d8e78f6f6..000000000
--- a/packages/kit/src/core/dev/amp_hook.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/** @type {import('amphtml-validator').Validator} */
-const amp = await (await import('amphtml-validator')).getInstance();
-
-/** @type {import('types').Handle} */
-export async function handle({ event, resolve }) {
-	const response = await resolve(event);
-	if (response.headers.get('content-type') !== 'text/html') {
-		return response;
-	}
-
-	let rendered = await response.text();
-	const result = amp.validateString(rendered);
-
-	if (result.status !== 'PASS') {
-		const lines = rendered.split('\n');
-
-		/** @param {string} str */
-		const escape = (str) => str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
-
-		rendered = `<!doctype html>
-		<head>
-			<meta charset="utf-8" />
-			<meta name="viewport" content="width=device-width, initial-scale=1" />
-			<style>
-				body {
-					font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
-					color: #333;
-				}
-
-				pre {
-					background: #f4f4f4;
-					padding: 1em;
-					overflow-x: auto;
-				}
-			</style>
-		</head>
-		<h1>AMP validation failed</h1>
-
-		${result.errors
-			.map(
-				(error) => `
-			<h2>${error.severity}</h2>
-			<p>Line ${error.line}, column ${error.col}: ${error.message} (<a href="${error.specUrl}">${
-					error.code
-				}</a>)</p>
-			<pre>${escape(lines[error.line - 1])}</pre>
-		`
-			)
-			.join('\n\n')}
-	`;
-	}
-
-	return new Response(rendered, { status: response.status, headers: response.headers });
-}
diff --git a/packages/kit/src/core/dev/index.js b/packages/kit/src/core/dev/index.js
index 453f6c36e..350bff1c7 100644
--- a/packages/kit/src/core/dev/index.js
+++ b/packages/kit/src/core/dev/index.js
@@ -64,12 +64,7 @@ export async function dev({ cwd, port, host, https, config }) {
 		plugins: [
 			svelte({
 				...config,
-				// In AMP mode, we know that there are no conditional component imports. In that case, we
-				// don't need to include CSS for components that are imported but unused, so we can just
-				// include rendered CSS.
-				// This would also apply if hydrate and router are both false, but we don't know if one
-				// has been enabled at the page level, so we don't do anything there.
-				emitCss: !config.kit.amp,
+				emitCss: true,
 				compilerOptions: {
 					...config.compilerOptions,
 					hydratable: !!config.kit.browser.hydrate
diff --git a/packages/kit/src/core/dev/plugin.js b/packages/kit/src/core/dev/plugin.js
index a589330de..ea900c7a0 100644
--- a/packages/kit/src/core/dev/plugin.js
+++ b/packages/kit/src/core/dev/plugin.js
@@ -10,7 +10,6 @@ import { SVELTE_KIT_ASSETS } from '../constants.js';
 import { get_mime_lookup, get_runtime_path, resolve_entry } from '../utils.js';
 import { coalesce_to_error } from '../../utils/error.js';
 import { load_template } from '../config/index.js';
-import { sequence } from '../../hooks.js';
 import { posixify } from '../../utils/filesystem.js';
 import { parse_route_id } from '../../utils/routing.js';
 
@@ -26,14 +25,6 @@ const style_pattern = /\.(css|less|sass|scss|styl|stylus|pcss|postcss)$/;
 export async function create_plugin(config, cwd) {
 	const runtime = get_runtime_path(config);
 
-	/** @type {import('types').Handle} */
-	let amp;
-
-	if (config.kit.amp) {
-		process.env.VITE_SVELTEKIT_AMP = 'true';
-		amp = (await import('./amp_hook.js')).handle;
-	}
-
 	process.env.VITE_SVELTEKIT_APP_VERSION_POLL_INTERVAL = '0';
 
 	/** @type {import('types').Respond} */
@@ -181,6 +172,11 @@ export async function create_plugin(config, cwd) {
 			});
 
 			return () => {
+				const serve_static_middleware = vite.middlewares.stack.find(
+					(middleware) =>
+						/** @type {function} */ (middleware.handle).name === 'viteServeStaticMiddleware'
+				);
+
 				remove_html_middlewares(vite.middlewares);
 
 				vite.middlewares.use(async (req, res) => {
@@ -220,7 +216,7 @@ export async function create_plugin(config, cwd) {
 						/** @type {import('types').Hooks} */
 						const hooks = {
 							getSession: user_hooks.getSession || (() => ({})),
-							handle: amp ? sequence(amp, handle) : handle,
+							handle,
 							handleError:
 								user_hooks.handleError ||
 								(({ /** @type {Error & { frame?: string }} */ error }) => {
@@ -282,7 +278,6 @@ export async function create_plugin(config, cwd) {
 						const rendered = await respond(
 							request,
 							{
-								amp: config.kit.amp,
 								csp: config.kit.csp,
 								dev: true,
 								floc: config.kit.floc,
@@ -346,10 +341,13 @@ export async function create_plugin(config, cwd) {
 							}
 						);
 
-						if (rendered) {
-							setResponse(res, rendered);
+						if (rendered.status === 404) {
+							// @ts-expect-error
+							serve_static_middleware.handle(req, res, () => {
+								setResponse(res, rendered);
+							});
 						} else {
-							not_found(res);
+							setResponse(res, rendered);
 						}
 					} catch (e) {
 						const error = coalesce_to_error(e);
@@ -376,7 +374,8 @@ function remove_html_middlewares(server) {
 	const html_middlewares = [
 		'viteIndexHtmlMiddleware',
 		'vite404Middleware',
-		'viteSpaFallbackMiddleware'
+		'viteSpaFallbackMiddleware',
+		'viteServeStaticMiddleware'
 	];
 	for (let i = server.stack.length - 1; i > 0; i--) {
 		// @ts-expect-error using internals until https://github.com/vitejs/vite/pull/4640 is merged
diff --git a/packages/kit/src/core/sync/write_tsconfig.js b/packages/kit/src/core/sync/write_tsconfig.js
index 92ba0b9c0..d231b35c3 100644
--- a/packages/kit/src/core/sync/write_tsconfig.js
+++ b/packages/kit/src/core/sync/write_tsconfig.js
@@ -8,11 +8,12 @@ import { write_if_changed } from './utils.js';
 const exists = (file) => fs.existsSync(file) && file;
 
 /** @param {import('types').ValidatedConfig} config */
-export function write_tsconfig(config) {
+export function write_tsconfig(config, cwd = process.cwd()) {
 	const out = path.join(config.kit.outDir, 'tsconfig.json');
-	const user_file = exists('tsconfig.json') || exists('jsconfig.json');
+	const user_file =
+		exists(path.resolve(cwd, 'tsconfig.json')) || exists(path.resolve(cwd, 'jsconfig.json'));
 
-	if (user_file) validate(config, out, user_file);
+	if (user_file) validate(config, cwd, out, user_file);
 
 	/** @param {string} file */
 	const project_relative = (file) => posixify(path.relative('.', file));
@@ -76,17 +77,20 @@ export function write_tsconfig(config) {
 
 /**
  * @param {import('types').ValidatedConfig} config
+ * @param {string} cwd
  * @param {string} out
  * @param {string} user_file
  */
-function validate(config, out, user_file) {
+function validate(config, cwd, out, user_file) {
 	// we have to eval the file, since it's not parseable as JSON (contains comments)
 	const user_tsconfig_json = fs.readFileSync(user_file, 'utf-8');
 	const user_tsconfig = (0, eval)(`(${user_tsconfig_json})`);
 
 	// we need to check that the user's tsconfig extends the framework config
 	const extend = user_tsconfig.extends;
-	const extends_framework_config = extend && path.resolve('.', extend) === out;
+	const extends_framework_config = extend && path.resolve(cwd, extend) === out;
+
+	const kind = path.basename(user_file);
 
 	if (extends_framework_config) {
 		const { paths: user_paths } = user_tsconfig.compilerOptions || {};
@@ -98,16 +102,16 @@ function validate(config, out, user_file) {
 			const lib_ = user_paths['$lib/*'] || [];
 
 			const missing_lib_paths =
-				!lib.some((relative) => path.resolve('.', relative) === config.kit.files.lib) ||
+				!lib.some((relative) => path.resolve(cwd, relative) === config.kit.files.lib) ||
 				!lib_.some(
-					(relative) => path.resolve('.', relative) === path.join(config.kit.files.lib, '/*')
+					(relative) => path.resolve(cwd, relative) === path.join(config.kit.files.lib, '/*')
 				);
 
 			if (missing_lib_paths) {
 				console.warn(
 					colors
 						.bold()
-						.yellow(`Your compilerOptions.paths in ${user_file} should include the following:`)
+						.yellow(`Your compilerOptions.paths in ${kind} should include the following:`)
 				);
 				const relative = posixify(path.relative('.', config.kit.files.lib));
 				console.warn(`{\n  "$lib":["${relative}"],\n  "$lib/*":["${relative}/*"]\n}`);
@@ -118,9 +122,7 @@ function validate(config, out, user_file) {
 		if (!relative.startsWith('./')) relative = './' + relative;
 
 		console.warn(
-			colors
-				.bold()
-				.yellow(`Your ${user_file} should extend the configuration generated by SvelteKit:`)
+			colors.bold().yellow(`Your ${kind} should extend the configuration generated by SvelteKit:`)
 		);
 		console.warn(`{\n  "extends": "${relative}"\n}`);
 	}
diff --git a/packages/kit/src/packaging/index.js b/packages/kit/src/packaging/index.js
index 010774db9..24414d643 100644
--- a/packages/kit/src/packaging/index.js
+++ b/packages/kit/src/packaging/index.js
@@ -26,7 +26,7 @@ export async function build(config, cwd = process.cwd()) {
 	mkdirp(dir);
 
 	// Make sure generated tsconfig is up-to-date
-	write_tsconfig(config);
+	write_tsconfig(config, cwd);
 
 	const files = scan(config);
 
diff --git a/packages/kit/src/runtime/app/env.js b/packages/kit/src/runtime/app/env.js
index f422aa509..0e868726b 100644
--- a/packages/kit/src/runtime/app/env.js
+++ b/packages/kit/src/runtime/app/env.js
@@ -10,9 +10,5 @@ export const dev = !!import.meta.env.DEV;
  * @type {import('$app/env').mode}
  */
 export const mode = import.meta.env.MODE;
-/**
- * @type {import('$app/env').amp}
- */
-export const amp = !!import.meta.env.VITE_SVELTEKIT_AMP;
 
 export { prerendering } from '../env.js';
diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js
index 8ee1be9e0..fc28a2310 100644
--- a/packages/kit/src/runtime/client/client.js
+++ b/packages/kit/src/runtime/client/client.js
@@ -48,6 +48,33 @@ function update_scroll_positions(index) {
 	scroll_positions[index] = scroll_state();
 }
 
+const fetch = window.fetch;
+let loading = 0;
+
+if (import.meta.env.DEV) {
+	let can_inspect_stack_trace = false;
+
+	const check_stack_trace = async () => {
+		const stack = /** @type {string} */ (new Error().stack);
+		can_inspect_stack_trace = stack.includes('check_stack_trace');
+	};
+
+	check_stack_trace();
+
+	window.fetch = (input, init) => {
+		const url = input instanceof Request ? input.url : input.toString();
+		const stack = /** @type {string} */ (new Error().stack);
+
+		const heuristic = can_inspect_stack_trace ? stack.includes('load_node') : loading;
+		if (heuristic) {
+			console.warn(
+				`Loading ${url} using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://kit.svelte.dev/docs/loading#input-fetch`
+			);
+		}
+		return fetch(input, init);
+	};
+}
+
 /**
  * @param {{
  *   target: Element;
@@ -530,7 +557,18 @@ export function create_client({ target, session, base, trailing_slash }) {
 				props: props || {},
 				get url() {
 					node.uses.url = true;
-					return url;
+
+					return new Proxy(url, {
+						get: (target, property) => {
+							if (property === 'hash') {
+								throw new Error(
+									'url.hash is inaccessible from load. Consider accessing hash from the page store within the script tag of your component.'
+								);
+							}
+
+							return Reflect.get(target, property, target);
+						}
+					});
 				},
 				get session() {
 					node.uses.session = true;
@@ -559,7 +597,18 @@ export function create_client({ target, session, base, trailing_slash }) {
 				});
 			}
 
-			const loaded = await module.load.call(null, load_input);
+			let loaded;
+
+			if (import.meta.env.DEV) {
+				try {
+					loading += 1;
+					loaded = await module.load.call(null, load_input);
+				} finally {
+					loading -= 1;
+				}
+			} else {
+				loaded = await module.load.call(null, load_input);
+			}
 
 			if (!loaded) {
 				throw new Error('load function must return a value');
diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js
index 34f0f9a91..c3156d8c9 100644
--- a/packages/kit/src/runtime/server/page/render.js
+++ b/packages/kit/src/runtime/server/page/render.js
@@ -188,95 +188,76 @@ export async function render_response({
 		}
 	`;
 
-	if (options.amp) {
-		// inline_style contains CSS files (i.e. `import './styles.css'`)
-		// rendered.css contains the CSS from `<style>` tags in Svelte components
-		const styles = `${inlined_style}\n${rendered.css.code}`;
-		head += `
-		<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style>
-		<noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
-		<script async src="https://cdn.ampproject.org/v0.js"></script>
-
-		<style amp-custom>${styles}</style>`;
-
-		if (options.service_worker) {
-			head +=
-				'<script async custom-element="amp-install-serviceworker" src="https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js"></script>';
-
-			body += `<amp-install-serviceworker src="${options.service_worker}" layout="nodisplay"></amp-install-serviceworker>`;
-		}
-	} else {
-		if (inlined_style) {
-			const attributes = [];
-			if (options.dev) attributes.push(' data-sveltekit');
-			if (csp.style_needs_nonce) attributes.push(` nonce="${csp.nonce}"`);
+	if (inlined_style) {
+		const attributes = [];
+		if (options.dev) attributes.push(' data-sveltekit');
+		if (csp.style_needs_nonce) attributes.push(` nonce="${csp.nonce}"`);
 
-			csp.add_style(inlined_style);
+		csp.add_style(inlined_style);
 
-			head += `\n\t<style${attributes.join('')}>${inlined_style}</style>`;
-		}
+		head += `\n\t<style${attributes.join('')}>${inlined_style}</style>`;
+	}
 
-		// prettier-ignore
-		head += Array.from(stylesheets)
-			.map((dep) => {
-				const attributes = [
-					'rel="stylesheet"',
-					`href="${options.prefix + dep}"`
-				];
+	// prettier-ignore
+	head += Array.from(stylesheets)
+		.map((dep) => {
+			const attributes = [
+				'rel="stylesheet"',
+				`href="${options.prefix + dep}"`
+			];
+
+			if (csp.style_needs_nonce) {
+				attributes.push(`nonce="${csp.nonce}"`);
+			}
 
-				if (csp.style_needs_nonce) {
-					attributes.push(`nonce="${csp.nonce}"`);
-				}
+			if (styles.has(dep)) {
+				// don't load stylesheets that are already inlined
+				// include them in disabled state so that Vite can detect them and doesn't try to add them
+				attributes.push('disabled', 'media="(max-width: 0)"');
+			}
 
-				if (styles.has(dep)) {
-					// don't load stylesheets that are already inlined
-					// include them in disabled state so that Vite can detect them and doesn't try to add them
-					attributes.push('disabled', 'media="(max-width: 0)"');
-				}
+			return `\n\t<link ${attributes.join(' ')}>`;
+		})
+		.join('');
 
-				return `\n\t<link ${attributes.join(' ')}>`;
-			})
+	if (page_config.router || page_config.hydrate) {
+		head += Array.from(modulepreloads)
+			.map((dep) => `\n\t<link rel="modulepreload" href="${options.prefix + dep}">`)
 			.join('');
 
-		if (page_config.router || page_config.hydrate) {
-			head += Array.from(modulepreloads)
-				.map((dep) => `\n\t<link rel="modulepreload" href="${options.prefix + dep}">`)
-				.join('');
-
-			const attributes = ['type="module"', `data-sveltekit-hydrate="${target}"`];
+		const attributes = ['type="module"', `data-sveltekit-hydrate="${target}"`];
 
-			csp.add_script(init_app);
+		csp.add_script(init_app);
 
-			if (csp.script_needs_nonce) {
-				attributes.push(`nonce="${csp.nonce}"`);
-			}
+		if (csp.script_needs_nonce) {
+			attributes.push(`nonce="${csp.nonce}"`);
+		}
 
-			body += `\n\t\t<script ${attributes.join(' ')}>${init_app}</script>`;
+		body += `\n\t\t<script ${attributes.join(' ')}>${init_app}</script>`;
 
-			body += serialized_data
-				.map(({ url, body, response }) =>
-					render_json_payload_script(
-						{ type: 'data', url, body: typeof body === 'string' ? hash(body) : undefined },
-						response
-					)
+		body += serialized_data
+			.map(({ url, body, response }) =>
+				render_json_payload_script(
+					{ type: 'data', url, body: typeof body === 'string' ? hash(body) : undefined },
+					response
 				)
-				.join('\n\t');
+			)
+			.join('\n\t');
 
-			if (shadow_props) {
-				body += render_json_payload_script({ type: 'props' }, shadow_props);
-			}
+		if (shadow_props) {
+			body += render_json_payload_script({ type: 'props' }, shadow_props);
 		}
+	}
 
-		if (options.service_worker) {
-			// always include service worker unless it's turned off explicitly
-			csp.add_script(init_service_worker);
+	if (options.service_worker) {
+		// always include service worker unless it's turned off explicitly
+		csp.add_script(init_service_worker);
 
-			head += `
-				<script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`;
-		}
+		head += `
+			<script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`;
 	}
 
-	if (state.prerender && !options.amp) {
+	if (state.prerender) {
 		const http_equiv = [];
 
 		const csp_headers = csp.get_meta();
diff --git a/packages/kit/src/utils/filesystem.js b/packages/kit/src/utils/filesystem.js
index fbc201f40..dc63b0ed8 100644
--- a/packages/kit/src/utils/filesystem.js
+++ b/packages/kit/src/utils/filesystem.js
@@ -13,7 +13,7 @@ export function mkdirp(dir) {
 
 /** @param {string} path */
 export function rimraf(path) {
-	(fs.rmSync || fs.rmdirSync)(path, { recursive: true, force: true });
+	fs.rmSync(path, { force: true, recursive: true });
 }
 
 /**
diff --git a/packages/kit/types/ambient.d.ts b/packages/kit/types/ambient.d.ts
index 9847bc59c..5d1e7da7b 100644
--- a/packages/kit/types/ambient.d.ts
+++ b/packages/kit/types/ambient.d.ts
@@ -49,14 +49,10 @@ declare namespace App {
 
 /**
  * ```ts
- * import { amp, browser, dev, mode, prerendering } from '$app/env';
+ * import { browser, dev, mode, prerendering } from '$app/env';
  * ```
  */
 declare module '$app/env' {
-	/**
-	 * Whether or not the app is running in [AMP mode](/docs/seo#manual-setup-amp).
-	 */
-	export const amp: boolean;
 	/**
 	 * Whether the app is running in the browser or on the server.
 	 */
diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts
index 4ce08a71e..302020246 100644
--- a/packages/kit/types/index.d.ts
+++ b/packages/kit/types/index.d.ts
@@ -93,7 +93,6 @@ export interface Config {
 	extensions?: string[];
 	kit?: {
 		adapter?: Adapter;
-		amp?: boolean;
 		appDir?: string;
 		browser?: {
 			hydrate?: boolean;
diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts
index 82f32402e..3d7e7b7de 100644
--- a/packages/kit/types/internal.d.ts
+++ b/packages/kit/types/internal.d.ts
@@ -240,7 +240,6 @@ export interface SSRNode {
 export type SSRNodeLoader = () => Promise<SSRNode>;
 
 export interface SSROptions {
-	amp: boolean;
 	csp: ValidatedConfig['kit']['csp'];
 	dev: boolean;
 	floc: boolean;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6dd4b5654..61bf09bc2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -195,6 +195,9 @@ importers:
       '@types/node': 14.18.17
       typescript: 4.6.2
 
+  packages/amp:
+    specifiers: {}
+
   packages/create-svelte:
     specifiers:
       '@playwright/test': ^1.21.0
@@ -254,7 +257,6 @@ importers:
       '@playwright/test': ^1.21.0
       '@rollup/plugin-replace': ^4.0.0
       '@sveltejs/vite-plugin-svelte': ^1.0.0-next.44
-      '@types/amphtml-validator': ^1.0.1
       '@types/connect': ^3.4.35
       '@types/cookie': ^0.5.0
       '@types/marked': ^4.0.1
@@ -262,7 +264,6 @@ importers:
       '@types/node': ^16.11.11
       '@types/sade': ^1.7.3
       '@types/set-cookie-parser': ^2.4.2
-      amphtml-validator: ^1.0.35
       chokidar: ^3.5.3
       cookie: ^0.5.0
       cross-env: ^7.0.3
@@ -286,16 +287,15 @@ importers:
       tiny-glob: ^0.2.9
       typescript: ^4.6.4
       uvu: ^0.5.2
-      vite: ^2.9.0
+      vite: ^2.9.9
     dependencies:
-      '@sveltejs/vite-plugin-svelte': 1.0.0-next.44_svelte@3.44.2+vite@2.9.0
+      '@sveltejs/vite-plugin-svelte': 1.0.0-next.44_svelte@3.44.2+vite@2.9.9
       chokidar: 3.5.3
       sade: 1.7.4
-      vite: 2.9.0
+      vite: 2.9.9
     devDependencies:
       '@playwright/test': 1.21.0
       '@rollup/plugin-replace': 4.0.0_rollup@2.60.2
-      '@types/amphtml-validator': 1.0.1
       '@types/connect': 3.4.35
       '@types/cookie': 0.5.0
       '@types/marked': 4.0.1
@@ -303,7 +303,6 @@ importers:
       '@types/node': 16.11.11
       '@types/sade': 1.7.3
       '@types/set-cookie-parser': 2.4.2
-      amphtml-validator: 1.0.35
       cookie: 0.5.0
       cross-env: 7.0.3
       devalue: 2.0.1
@@ -328,14 +327,18 @@ importers:
 
   packages/kit/test/apps/amp:
     specifiers:
+      '@sveltejs/amp': workspace:*
       '@sveltejs/kit': workspace:*
       cross-env: ^7.0.3
+      purify-css: ^1.2.5
       svelte: ^3.43.0
       svelte-check: ^2.5.0
       typescript: ~4.6.2
     devDependencies:
+      '@sveltejs/amp': link:../../../../amp
       '@sveltejs/kit': link:../../..
       cross-env: 7.0.3
+      purify-css: 1.2.5
       svelte: 3.44.2
       svelte-check: 2.5.0_svelte@3.44.2
       typescript: 4.6.2
@@ -458,6 +461,7 @@ importers:
     specifiers:
       '@sveltejs/adapter-auto': workspace:*
       '@sveltejs/adapter-static': workspace:*
+      '@sveltejs/amp': workspace:*
       '@sveltejs/kit': workspace:*
       '@sveltejs/site-kit': ^2.1.0
       '@types/node': ^16.11.11
@@ -468,11 +472,12 @@ importers:
       shiki-twoslash: ^3.0.2
       svelte: ^3.43.0
       typescript: ~4.6.2
-      vite: ^2.9.0
+      vite: ^2.9.9
       vite-imagetools: ^4.0.3
     devDependencies:
       '@sveltejs/adapter-auto': link:../../packages/adapter-auto
       '@sveltejs/adapter-static': link:../../packages/adapter-static
+      '@sveltejs/amp': link:../../packages/amp
       '@sveltejs/kit': link:../../packages/kit
       '@sveltejs/site-kit': 2.1.0
       '@types/node': 16.11.11
@@ -483,7 +488,7 @@ importers:
       shiki-twoslash: 3.0.2
       svelte: 3.44.2
       typescript: 4.6.2
-      vite: 2.9.0
+      vite: 2.9.9
       vite-imagetools: 4.0.3
 
 packages:
@@ -1476,7 +1481,7 @@ packages:
       golden-fleece: 1.0.9
     dev: true
 
-  /@sveltejs/vite-plugin-svelte/1.0.0-next.44_svelte@3.44.2+vite@2.9.0:
+  /@sveltejs/vite-plugin-svelte/1.0.0-next.44_svelte@3.44.2+vite@2.9.9:
     resolution: {integrity: sha512-n+sssEWbzykPS447FmnNyU5GxEhrBPDVd0lxNZnxRGz9P6651LjjwAnISKr3CKgT9v8IybP8VD0n2i5XzbqExg==}
     engines: {node: ^14.13.1 || >= 16}
     peerDependencies:
@@ -1494,7 +1499,7 @@ packages:
       magic-string: 0.26.2
       svelte: 3.44.2
       svelte-hmr: 0.14.11_svelte@3.44.2
-      vite: 2.9.0
+      vite: 2.9.9
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -1509,12 +1514,6 @@ packages:
       - encoding
     dev: true
 
-  /@types/amphtml-validator/1.0.1:
-    resolution: {integrity: sha512-DWE7fy6KtC+Uw0KV/HAmjuH2GB/o8yskXlvmVWR7mOVsLDybp+XrwkzEeRFU9wGjWKeRMBNGsx+5DRq7sUsAwA==}
-    dependencies:
-      '@types/node': 16.11.11
-    dev: true
-
   /@types/body-parser/1.19.2:
     resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
     dependencies:
@@ -1892,15 +1891,6 @@ packages:
       uri-js: 4.4.1
     dev: true
 
-  /amphtml-validator/1.0.35:
-    resolution: {integrity: sha512-C67JzC5EI6pE2C0sAo/zuCp8ARDl1Vtt6/s0nr+3NuXDNOdkjclZUkaNAd/ZnsEvvYodkXZ6T/uww890IQh9dQ==}
-    hasBin: true
-    dependencies:
-      colors: 1.4.0
-      commander: 7.2.0
-      promise: 8.1.0
-    dev: true
-
   /ansi-align/2.0.0:
     resolution: {integrity: sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=}
     dependencies:
@@ -2008,8 +1998,10 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
-  /asap/2.0.6:
-    resolution: {integrity: sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=}
+  /atob/2.1.2:
+    resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==}
+    engines: {node: '>= 4.5.0'}
+    hasBin: true
     dev: true
 
   /babel-plugin-dynamic-import-node/2.3.3:
@@ -2215,11 +2207,26 @@ packages:
     resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==}
     dev: true
 
+  /clean-css/4.2.4:
+    resolution: {integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==}
+    engines: {node: '>= 4.0'}
+    dependencies:
+      source-map: 0.6.1
+    dev: true
+
   /cli-boxes/1.0.0:
     resolution: {integrity: sha1-T6kXw+WclKAEzWH47lCdplFocUM=}
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /cliui/3.2.0:
+    resolution: {integrity: sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=}
+    dependencies:
+      string-width: 1.0.2
+      strip-ansi: 3.0.1
+      wrap-ansi: 2.1.0
+    dev: true
+
   /cliui/6.0.0:
     resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
     dependencies:
@@ -2291,11 +2298,6 @@ packages:
     engines: {node: '>= 6'}
     dev: true
 
-  /commander/7.2.0:
-    resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
-    engines: {node: '>= 10'}
-    dev: true
-
   /commander/8.3.0:
     resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
     engines: {node: '>= 12'}
@@ -2335,6 +2337,10 @@ packages:
     resolution: {integrity: sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=}
     dev: true
 
+  /convert-source-map/0.3.5:
+    resolution: {integrity: sha1-8dgClQr33SYxof6+BZZVDIarMZA=}
+    dev: true
+
   /convert-source-map/1.8.0:
     resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==}
     dependencies:
@@ -2374,6 +2380,15 @@ packages:
       which: 2.0.2
     dev: true
 
+  /css/2.2.4:
+    resolution: {integrity: sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==}
+    dependencies:
+      inherits: 2.0.4
+      source-map: 0.6.1
+      source-map-resolve: 0.5.3
+      urix: 0.1.0
+    dev: true
+
   /csv-generate/3.4.3:
     resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==}
     dev: true
@@ -2463,6 +2478,11 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /decode-uri-component/0.2.0:
+    resolution: {integrity: sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=}
+    engines: {node: '>=0.10'}
+    dev: true
+
   /decompress-response/6.0.0:
     resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
     engines: {node: '>=10'}
@@ -3323,6 +3343,10 @@ packages:
     engines: {node: '>=6.9.0'}
     dev: true
 
+  /get-caller-file/1.0.3:
+    resolution: {integrity: sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==}
+    dev: true
+
   /get-caller-file/2.0.5:
     resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
     engines: {node: 6.* || 8.* || >= 10.*}
@@ -3583,6 +3607,11 @@ packages:
       side-channel: 1.0.4
     dev: true
 
+  /invert-kv/1.0.0:
+    resolution: {integrity: sha1-EEqOSqym09jNFXqO+L+rLXo//bY=}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
   /ip/1.1.5:
     resolution: {integrity: sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=}
     dev: true
@@ -3922,6 +3951,13 @@ packages:
     resolution: {integrity: sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==}
     engines: {node: '>=6'}
 
+  /lcid/1.0.0:
+    resolution: {integrity: sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      invert-kv: 1.0.0
+    dev: true
+
   /levn/0.4.1:
     resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
     engines: {node: '>= 0.8.0'}
@@ -3934,6 +3970,16 @@ packages:
     resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
     dev: true
 
+  /load-json-file/2.0.0:
+    resolution: {integrity: sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=}
+    engines: {node: '>=4'}
+    dependencies:
+      graceful-fs: 4.2.10
+      parse-json: 2.2.0
+      pify: 2.3.0
+      strip-bom: 3.0.0
+    dev: true
+
   /load-yaml-file/0.2.0:
     resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==}
     engines: {node: '>=6'}
@@ -4045,6 +4091,13 @@ packages:
     hasBin: true
     dev: true
 
+  /mem/1.1.0:
+    resolution: {integrity: sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=}
+    engines: {node: '>=4'}
+    dependencies:
+      mimic-fn: 1.2.0
+    dev: true
+
   /meow/6.1.1:
     resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==}
     engines: {node: '>=8'}
@@ -4101,6 +4154,11 @@ packages:
     hasBin: true
     dev: true
 
+  /mimic-fn/1.2.0:
+    resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==}
+    engines: {node: '>=4'}
+    dev: true
+
   /mimic-response/3.1.0:
     resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
     engines: {node: '>=10'}
@@ -4173,8 +4231,8 @@ packages:
       thenify-all: 1.6.0
     dev: true
 
-  /nanoid/3.3.2:
-    resolution: {integrity: sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==}
+  /nanoid/3.3.4:
+    resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
     hasBin: true
 
@@ -4349,6 +4407,15 @@ packages:
       word-wrap: 1.2.3
     dev: true
 
+  /os-locale/2.1.0:
+    resolution: {integrity: sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==}
+    engines: {node: '>=4'}
+    dependencies:
+      execa: 0.7.0
+      lcid: 1.0.0
+      mem: 1.1.0
+    dev: true
+
   /os-tmpdir/1.0.2:
     resolution: {integrity: sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=}
     engines: {node: '>=0.10.0'}
@@ -4434,6 +4501,13 @@ packages:
       callsites: 3.1.0
     dev: true
 
+  /parse-json/2.2.0:
+    resolution: {integrity: sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      error-ex: 1.3.2
+    dev: true
+
   /parse-json/5.2.0:
     resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
     engines: {node: '>=8'}
@@ -4479,6 +4553,13 @@ packages:
   /path-parse/1.0.7:
     resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
 
+  /path-type/2.0.0:
+    resolution: {integrity: sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=}
+    engines: {node: '>=4'}
+    dependencies:
+      pify: 2.3.0
+    dev: true
+
   /path-type/4.0.0:
     resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
     engines: {node: '>=8'}
@@ -4500,6 +4581,11 @@ packages:
     resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
     engines: {node: '>=8.6'}
 
+  /pify/2.3.0:
+    resolution: {integrity: sha1-7RQaasBDqEnqWISY59yosVMw6Qw=}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
   /pify/4.0.1:
     resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
     engines: {node: '>=6'}
@@ -4602,11 +4688,11 @@ packages:
     resolution: {integrity: sha512-me2dL+chJVb88zpE228MvA6wIRy1CuXxGTwI5hYe4DnSnXRbtJT+9ggRj+49kgHgs/AKMTKOt/EkTHSvQJmRXA==}
     dev: true
 
-  /postcss/8.4.12:
-    resolution: {integrity: sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==}
+  /postcss/8.4.14:
+    resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==}
     engines: {node: ^10 || ^12 || >=14}
     dependencies:
-      nanoid: 3.3.2
+      nanoid: 3.3.4
       picocolors: 1.0.0
       source-map-js: 1.0.2
 
@@ -4694,12 +4780,6 @@ packages:
     engines: {node: '>=0.4.0'}
     dev: true
 
-  /promise/8.1.0:
-    resolution: {integrity: sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==}
-    dependencies:
-      asap: 2.0.6
-    dev: true
-
   /prompts/2.4.2:
     resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
     engines: {node: '>= 6'}
@@ -4736,6 +4816,17 @@ packages:
     engines: {node: '>=6'}
     dev: true
 
+  /purify-css/1.2.5:
+    resolution: {integrity: sha512-Vy4jRnV2w/kUjTyxzQOKbFkqwUe6RNLuZgIWR/IRQ8nCqRwiFgwC9XiO9+8poq5KL053uWAQnCSbsfihq77zPg==}
+    hasBin: true
+    dependencies:
+      clean-css: 4.2.4
+      glob: 7.2.0
+      rework: 1.0.1
+      uglify-js: 3.15.4
+      yargs: 8.0.2
+    dev: true
+
   /queue-microtask/1.2.3:
     resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
     dev: true
@@ -4759,6 +4850,14 @@ packages:
     resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
     dev: true
 
+  /read-pkg-up/2.0.0:
+    resolution: {integrity: sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=}
+    engines: {node: '>=4'}
+    dependencies:
+      find-up: 2.1.0
+      read-pkg: 2.0.0
+    dev: true
+
   /read-pkg-up/7.0.1:
     resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
     engines: {node: '>=8'}
@@ -4768,6 +4867,15 @@ packages:
       type-fest: 0.8.1
     dev: true
 
+  /read-pkg/2.0.0:
+    resolution: {integrity: sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=}
+    engines: {node: '>=4'}
+    dependencies:
+      load-json-file: 2.0.0
+      normalize-package-data: 2.5.0
+      path-type: 2.0.0
+    dev: true
+
   /read-pkg/5.2.0:
     resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==}
     engines: {node: '>=8'}
@@ -4847,6 +4955,10 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /require-main-filename/1.0.1:
+    resolution: {integrity: sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=}
+    dev: true
+
   /require-main-filename/2.0.0:
     resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
     dev: true
@@ -4861,6 +4973,11 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
+  /resolve-url/0.2.1:
+    resolution: {integrity: sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=}
+    deprecated: https://github.com/lydell/resolve-url#deprecated
+    dev: true
+
   /resolve/1.20.0:
     resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==}
     dependencies:
@@ -4886,6 +5003,13 @@ packages:
     engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
     dev: true
 
+  /rework/1.0.1:
+    resolution: {integrity: sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=}
+    dependencies:
+      convert-source-map: 0.3.5
+      css: 2.2.4
+    dev: true
+
   /rimraf/2.7.1:
     resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
     hasBin: true
@@ -5133,17 +5257,38 @@ packages:
     resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
     engines: {node: '>=0.10.0'}
 
+  /source-map-resolve/0.5.3:
+    resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==}
+    deprecated: See https://github.com/lydell/source-map-resolve#deprecated
+    dependencies:
+      atob: 2.1.2
+      decode-uri-component: 0.2.0
+      resolve-url: 0.2.1
+      source-map-url: 0.4.1
+      urix: 0.1.0
+    dev: true
+
   /source-map-support/0.4.18:
     resolution: {integrity: sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==}
     dependencies:
       source-map: 0.5.7
     dev: true
 
+  /source-map-url/0.4.1:
+    resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==}
+    deprecated: See https://github.com/lydell/source-map-url#deprecated
+    dev: true
+
   /source-map/0.5.7:
     resolution: {integrity: sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=}
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /source-map/0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
   /source-map/0.7.3:
     resolution: {integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==}
     engines: {node: '>= 8'}
@@ -5935,6 +6080,12 @@ packages:
     hasBin: true
     dev: true
 
+  /uglify-js/3.15.4:
+    resolution: {integrity: sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==}
+    engines: {node: '>=0.8.0'}
+    hasBin: true
+    dev: true
+
   /unbox-primitive/1.0.1:
     resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==}
     dependencies:
@@ -5955,6 +6106,11 @@ packages:
       punycode: 2.1.1
     dev: true
 
+  /urix/0.1.0:
+    resolution: {integrity: sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=}
+    deprecated: Please see https://github.com/lydell/urix#deprecated
+    dev: true
+
   /util-deprecate/1.0.2:
     resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=}
     dev: true
@@ -6005,8 +6161,8 @@ packages:
       magic-string: 0.25.7
     dev: true
 
-  /vite/2.9.0:
-    resolution: {integrity: sha512-5NAnNqzPmZzJvrswZGeTS2JHrBGIzIWJA2hBTTMYuoBVEMh0xwE0b5yyIXFxf7F07hrK4ugX2LJ7q6t7iIbd4Q==}
+  /vite/2.9.9:
+    resolution: {integrity: sha512-ffaam+NgHfbEmfw/Vuh6BHKKlI/XIAhxE5QSS7gFLIngxg171mg1P3a4LSRME0z2ZU1ScxoKzphkipcYwSD5Ew==}
     engines: {node: '>=12.2.0'}
     hasBin: true
     peerDependencies:
@@ -6022,7 +6178,7 @@ packages:
         optional: true
     dependencies:
       esbuild: 0.14.29
-      postcss: 8.4.12
+      postcss: 8.4.14
       resolve: 1.22.0
       rollup: 2.60.2
     optionalDependencies:
@@ -6117,6 +6273,14 @@ packages:
       regexparam: 2.0.0
     dev: false
 
+  /wrap-ansi/2.1.0:
+    resolution: {integrity: sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      string-width: 1.0.2
+      strip-ansi: 3.0.1
+    dev: true
+
   /wrap-ansi/6.2.0:
     resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
     engines: {node: '>=8'}
@@ -6152,6 +6316,10 @@ packages:
         optional: true
     dev: true
 
+  /y18n/3.2.2:
+    resolution: {integrity: sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==}
+    dev: true
+
   /y18n/4.0.3:
     resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
     dev: true
@@ -6186,6 +6354,12 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
+  /yargs-parser/7.0.0:
+    resolution: {integrity: sha1-jQrELxbqVd69MyyvTEA4s+P139k=}
+    dependencies:
+      camelcase: 4.1.0
+    dev: true
+
   /yargs/15.4.1:
     resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
     engines: {node: '>=8'}
@@ -6216,6 +6390,24 @@ packages:
       yargs-parser: 20.2.9
     dev: true
 
+  /yargs/8.0.2:
+    resolution: {integrity: sha1-YpmpBVsc78lp/355wdkY3Osiw2A=}
+    dependencies:
+      camelcase: 4.1.0
+      cliui: 3.2.0
+      decamelize: 1.2.0
+      get-caller-file: 1.0.3
+      os-locale: 2.1.0
+      read-pkg-up: 2.0.0
+      require-directory: 2.1.1
+      require-main-filename: 1.0.1
+      set-blocking: 2.0.0
+      string-width: 2.1.1
+      which-module: 2.0.0
+      y18n: 3.2.2
+      yargs-parser: 7.0.0
+    dev: true
+
   /yauzl/2.10.0:
     resolution: {integrity: sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=}
     dependencies:
diff --git a/sites/kit.svelte.dev/package.json b/sites/kit.svelte.dev/package.json
index 8f75148b2..3e280f174 100644
--- a/sites/kit.svelte.dev/package.json
+++ b/sites/kit.svelte.dev/package.json
@@ -10,6 +10,7 @@
 	"devDependencies": {
 		"@sveltejs/adapter-auto": "workspace:*",
 		"@sveltejs/adapter-static": "workspace:*",
+		"@sveltejs/amp": "workspace:*",
 		"@sveltejs/kit": "workspace:*",
 		"@sveltejs/site-kit": "^2.1.0",
 		"@types/node": "^16.11.11",
@@ -20,7 +21,7 @@
 		"shiki-twoslash": "^3.0.2",
 		"svelte": "^3.43.0",
 		"typescript": "~4.6.2",
-		"vite": "^2.9.0",
+		"vite": "^2.9.9",
 		"vite-imagetools": "^4.0.3"
 	},
 	"type": "module"


Test patch:
diff --git a/packages/kit/src/packaging/test/errors/duplicate-export/tsconfig.json b/packages/kit/src/packaging/test/errors/duplicate-export/tsconfig.json
new file mode 100644
index 000000000..81ff9770c
--- /dev/null
+++ b/packages/kit/src/packaging/test/errors/duplicate-export/tsconfig.json
@@ -0,0 +1,3 @@
+{
+	"extends": "./.svelte-kit/tsconfig.json"
+}
diff --git a/packages/kit/src/packaging/test/errors/no-lib-folder/tsconfig.json b/packages/kit/src/packaging/test/errors/no-lib-folder/tsconfig.json
new file mode 100644
index 000000000..81ff9770c
--- /dev/null
+++ b/packages/kit/src/packaging/test/errors/no-lib-folder/tsconfig.json
@@ -0,0 +1,3 @@
+{
+	"extends": "./.svelte-kit/tsconfig.json"
+}
diff --git a/packages/kit/src/packaging/test/fixtures/assets/jsconfig.json b/packages/kit/src/packaging/test/fixtures/assets/jsconfig.json
index 92575debb..81ff9770c 100644
--- a/packages/kit/src/packaging/test/fixtures/assets/jsconfig.json
+++ b/packages/kit/src/packaging/test/fixtures/assets/jsconfig.json
@@ -1,10 +1,3 @@
 {
-	"compilerOptions": {
-		"checkJs": true,
-		"baseUrl": ".",
-		"paths": {
-			"$lib/*": ["src/lib/*"]
-		}
-	},
-	"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
+	"extends": "./.svelte-kit/tsconfig.json"
 }
diff --git a/packages/kit/src/packaging/test/fixtures/exports-merge/jsconfig.json b/packages/kit/src/packaging/test/fixtures/exports-merge/jsconfig.json
index 92575debb..81ff9770c 100644
--- a/packages/kit/src/packaging/test/fixtures/exports-merge/jsconfig.json
+++ b/packages/kit/src/packaging/test/fixtures/exports-merge/jsconfig.json
@@ -1,10 +1,3 @@
 {
-	"compilerOptions": {
-		"checkJs": true,
-		"baseUrl": ".",
-		"paths": {
-			"$lib/*": ["src/lib/*"]
-		}
-	},
-	"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
+	"extends": "./.svelte-kit/tsconfig.json"
 }
diff --git a/packages/kit/src/packaging/test/fixtures/exports-replace/jsconfig.json b/packages/kit/src/packaging/test/fixtures/exports-replace/jsconfig.json
index 92575debb..81ff9770c 100644
--- a/packages/kit/src/packaging/test/fixtures/exports-replace/jsconfig.json
+++ b/packages/kit/src/packaging/test/fixtures/exports-replace/jsconfig.json
@@ -1,10 +1,3 @@
 {
-	"compilerOptions": {
-		"checkJs": true,
-		"baseUrl": ".",
-		"paths": {
-			"$lib/*": ["src/lib/*"]
-		}
-	},
-	"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
+	"extends": "./.svelte-kit/tsconfig.json"
 }
diff --git a/packages/kit/src/packaging/test/fixtures/files-exclude/jsconfig.json b/packages/kit/src/packaging/test/fixtures/files-exclude/jsconfig.json
index 92575debb..81ff9770c 100644
--- a/packages/kit/src/packaging/test/fixtures/files-exclude/jsconfig.json
+++ b/packages/kit/src/packaging/test/fixtures/files-exclude/jsconfig.json
@@ -1,10 +1,3 @@
 {
-	"compilerOptions": {
-		"checkJs": true,
-		"baseUrl": ".",
-		"paths": {
-			"$lib/*": ["src/lib/*"]
-		}
-	},
-	"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
+	"extends": "./.svelte-kit/tsconfig.json"
 }
diff --git a/packages/kit/src/packaging/test/fixtures/javascript/jsconfig.json b/packages/kit/src/packaging/test/fixtures/javascript/jsconfig.json
index 92575debb..81ff9770c 100644
--- a/packages/kit/src/packaging/test/fixtures/javascript/jsconfig.json
+++ b/packages/kit/src/packaging/test/fixtures/javascript/jsconfig.json
@@ -1,10 +1,3 @@
 {
-	"compilerOptions": {
-		"checkJs": true,
-		"baseUrl": ".",
-		"paths": {
-			"$lib/*": ["src/lib/*"]
-		}
-	},
-	"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
+	"extends": "./.svelte-kit/tsconfig.json"
 }
diff --git a/packages/kit/src/packaging/test/fixtures/resolve-alias/tsconfig.json b/packages/kit/src/packaging/test/fixtures/resolve-alias/tsconfig.json
index b011e4d18..81ff9770c 100644
--- a/packages/kit/src/packaging/test/fixtures/resolve-alias/tsconfig.json
+++ b/packages/kit/src/packaging/test/fixtures/resolve-alias/tsconfig.json
@@ -1,30 +1,3 @@
 {
-	"compilerOptions": {
-		"moduleResolution": "node",
-		"module": "es2020",
-		"lib": ["es2020", "DOM"],
-		"target": "es2020",
-		/**
-			svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
-			to enforce using \`import type\` instead of \`import\` for Types.
-			*/
-		"importsNotUsedAsValues": "error",
-		"isolatedModules": true,
-		"resolveJsonModule": true,
-		/**
-			To have warnings/errors of the Svelte compiler at the correct position,
-			enable source maps by default.
-			*/
-		"sourceMap": true,
-		"esModuleInterop": true,
-		"skipLibCheck": true,
-		"forceConsistentCasingInFileNames": true,
-		"baseUrl": ".",
-		"allowJs": true,
-		"checkJs": true,
-		"paths": {
-			"$lib/*": ["src/lib/*"]
-		}
-	},
-	"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"]
+	"extends": "./.svelte-kit/tsconfig.json"
 }
diff --git a/packages/kit/src/packaging/test/fixtures/typescript/tsconfig.json b/packages/kit/src/packaging/test/fixtures/typescript/tsconfig.json
index b011e4d18..81ff9770c 100644
--- a/packages/kit/src/packaging/test/fixtures/typescript/tsconfig.json
+++ b/packages/kit/src/packaging/test/fixtures/typescript/tsconfig.json
@@ -1,30 +1,3 @@
 {
-	"compilerOptions": {
-		"moduleResolution": "node",
-		"module": "es2020",
-		"lib": ["es2020", "DOM"],
-		"target": "es2020",
-		/**
-			svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
-			to enforce using \`import type\` instead of \`import\` for Types.
-			*/
-		"importsNotUsedAsValues": "error",
-		"isolatedModules": true,
-		"resolveJsonModule": true,
-		/**
-			To have warnings/errors of the Svelte compiler at the correct position,
-			enable source maps by default.
-			*/
-		"sourceMap": true,
-		"esModuleInterop": true,
-		"skipLibCheck": true,
-		"forceConsistentCasingInFileNames": true,
-		"baseUrl": ".",
-		"allowJs": true,
-		"checkJs": true,
-		"paths": {
-			"$lib/*": ["src/lib/*"]
-		}
-	},
-	"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"]
+	"extends": "./.svelte-kit/tsconfig.json"
 }
diff --git a/packages/kit/src/packaging/test/watch/expected/b.js b/packages/kit/src/packaging/test/watch/expected/b.js
index 46c8d9438..59d168993 100644
--- a/packages/kit/src/packaging/test/watch/expected/b.js
+++ b/packages/kit/src/packaging/test/watch/expected/b.js
@@ -1,4 +1,1 @@
-"use strict";
-Object.defineProperty(exports, "__esModule", { value: true });
-exports.b = void 0;
-exports.b = "b";
+export const b = "b";
diff --git a/packages/kit/src/packaging/test/watch/tsconfig.json b/packages/kit/src/packaging/test/watch/tsconfig.json
index 44dca20d0..4d654036b 100644
--- a/packages/kit/src/packaging/test/watch/tsconfig.json
+++ b/packages/kit/src/packaging/test/watch/tsconfig.json
@@ -3,5 +3,6 @@
 		"allowJs": true,
 		"checkJs": true,
 		"newLine": "lf"
-	}
+	},
+	"extends": "./.svelte-kit/tsconfig.json"
 }
diff --git a/packages/kit/test/apps/amp/package.json b/packages/kit/test/apps/amp/package.json
index 8cf523e3d..a66b74094 100644
--- a/packages/kit/test/apps/amp/package.json
+++ b/packages/kit/test/apps/amp/package.json
@@ -12,8 +12,10 @@
 		"test:build": "playwright test"
 	},
 	"devDependencies": {
+		"@sveltejs/amp": "workspace:*",
 		"@sveltejs/kit": "workspace:*",
 		"cross-env": "^7.0.3",
+		"purify-css": "^1.2.5",
 		"svelte": "^3.43.0",
 		"svelte-check": "^2.5.0",
 		"typescript": "~4.6.2"
diff --git a/packages/kit/test/apps/amp/src/app.html b/packages/kit/test/apps/amp/src/app.html
index c462a3001..bfb96ec90 100644
--- a/packages/kit/test/apps/amp/src/app.html
+++ b/packages/kit/test/apps/amp/src/app.html
@@ -3,7 +3,7 @@
 	<head>
 		<meta charset="utf-8" />
 		<meta name="viewport" content="width=device-width, initial-scale=1" />
-		<link rel="canonical" href="." />
+		<link rel="canonical" href="https://example.com" />
 		%svelte.head%
 	</head>
 	<body>
diff --git a/packages/kit/test/apps/amp/src/hooks.js b/packages/kit/test/apps/amp/src/hooks.js
new file mode 100644
index 000000000..c75f6f291
--- /dev/null
+++ b/packages/kit/test/apps/amp/src/hooks.js
@@ -0,0 +1,26 @@
+import purify from 'purify-css';
+import * as amp from '@sveltejs/amp';
+
+/** @type {import('@sveltejs/kit').Handle} */
+export async function handle({ event, resolve }) {
+	const response = await resolve(event, {
+		transformPage: ({ html }) => {
+			html = amp.transform(html);
+
+			// remove unused CSS
+			let css = '';
+			const markup = html.replace(
+				/<style amp-custom([^>]*?)>([^]+?)<\/style>/,
+				(match, attributes, contents) => {
+					css = contents;
+					return `<style amp-custom${attributes}></style>`;
+				}
+			);
+
+			css = purify(markup, css);
+			return markup.replace('</style>', `${css}</style>`);
+		}
+	});
+
+	return response;
+}
diff --git a/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control.svelte b/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control.svelte
new file mode 100644
index 000000000..6be73f14d
--- /dev/null
+++ b/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control.svelte
@@ -0,0 +1,14 @@
+<script context="module">
+	export const prerender = true;
+
+	/** @type {import('./__types/cache-control')} */
+	export function load() {
+		return {
+			cache: {
+				maxage: 300
+			}
+		};
+	}
+</script>
+
+<h1>the cache-control headers should be removed from this page</h1>
diff --git a/packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet.svelte b/packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet.svelte
new file mode 100644
index 000000000..e6c9b2bdb
--- /dev/null
+++ b/packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet.svelte
@@ -0,0 +1,3 @@
+<svelte:head>
+	<link rel="stylesheet" href="https://example.com/whatever" />
+</svelte:head>
diff --git a/packages/kit/test/apps/amp/src/routes/styles/index.svelte b/packages/kit/test/apps/amp/src/routes/styles/index.svelte
index 8d066cbe6..08db544b1 100644
--- a/packages/kit/test/apps/amp/src/routes/styles/index.svelte
+++ b/packages/kit/test/apps/amp/src/routes/styles/index.svelte
@@ -1,16 +1,14 @@
 <script>
-	import { amp } from '$app/env';
 	import Unused from './Unused.svelte';
 	import './imported.css';
 </script>
 
-{#if amp}
+{#if Math.random() <= 1}
 	<p class="shouty">this text is rendered</p>
 {:else}
-	<Unused/>
+	<Unused />
 {/if}
 
-
 <style>
 	p {
 		color: #ff3e00;
diff --git a/packages/kit/test/apps/amp/src/routes/valid/index.svelte b/packages/kit/test/apps/amp/src/routes/valid/index.svelte
index f6a65bef2..a69b92bd2 100644
--- a/packages/kit/test/apps/amp/src/routes/valid/index.svelte
+++ b/packages/kit/test/apps/amp/src/routes/valid/index.svelte
@@ -9,7 +9,7 @@
 </script>
 
 <script>
-	import { browser, dev, amp } from '$app/env';
+	import { browser, dev } from '$app/env';
 
 	/** @type {number} */
 	export let answer;
@@ -17,7 +17,7 @@
 
 <h1>Hello from the {browser ? 'client' : 'server'} in {dev ? 'dev' : 'prod'} mode!</h1>
 <h2>The answer is {answer}</h2>
-<p>amp is {amp}</p>
+<p>This text is red</p>
 
 <style>
 	p {
diff --git a/packages/kit/test/apps/amp/svelte.config.js b/packages/kit/test/apps/amp/svelte.config.js
index a0902eb13..9c3a2801c 100644
--- a/packages/kit/test/apps/amp/svelte.config.js
+++ b/packages/kit/test/apps/amp/svelte.config.js
@@ -1,9 +1,21 @@
+import path from 'path';
+
 /** @type {import('@sveltejs/kit').Config} */
 const config = {
 	kit: {
-		amp: true,
+		browser: {
+			router: false,
+			hydrate: false
+		},
+
+		inlineStyleThreshold: Infinity,
+
 		vite: {
 			server: {
+				fs: {
+					allow: [path.resolve('../../../src')]
+				},
+
 				// TODO: required to support ipv6, remove on vite 3
 				// https://github.com/vitejs/vite/issues/7075
 				host: 'localhost'
diff --git a/packages/kit/test/apps/amp/test/test.js b/packages/kit/test/apps/amp/test/test.js
index d63985c05..2497cee43 100644
--- a/packages/kit/test/apps/amp/test/test.js
+++ b/packages/kit/test/apps/amp/test/test.js
@@ -1,7 +1,7 @@
 import { expect } from '@playwright/test';
 import { test } from '../../../utils.js';
 
-test('amp is true', async ({ page, baseURL }) => {
+test('renders an AMP page', async ({ page, baseURL }) => {
 	await page.goto(`${baseURL}/valid`);
 
 	await expect(page.locator('h1')).toHaveText(
@@ -9,7 +9,6 @@ test('amp is true', async ({ page, baseURL }) => {
 	);
 
 	await expect(page.locator('h2')).toHaveText('The answer is 42');
-	await expect(page.locator('p')).toHaveText('amp is true');
 
 	// should not include serialized data
 	expect(await page.$('script[sveltekit\\:data-type="data"]')).toBeNull();
@@ -33,22 +32,6 @@ test('styles are applied', async ({ page, baseURL }) => {
 	).toEqual('rgb(128, 0, 128)');
 });
 
-test('prints validation errors', async ({ page, baseURL }) => {
-	await page.goto(`${baseURL}/invalid`);
-
-	const body = await page.innerHTML('body');
-
-	if (process.env.DEV) {
-		const h1 = page.locator('h1');
-		await expect(h1).toHaveText('AMP validation failed');
-
-		expect(body).toContain("Invalid URL protocol 'javascript:' for attribute 'href' in tag 'a'");
-		expect(body).toContain('&lt;a href="javascript:void(0);"&gt;invalid&lt;/a&gt;');
-	} else {
-		expect(body).toContain('<a href="javascript:void(0);">invalid</a>');
-	}
-});
-
 test('sets origin', async ({ baseURL, page }) => {
 	const { origin } = new URL(/** @type {string} */ (baseURL));
 
@@ -68,3 +51,31 @@ test('only includes CSS for rendered components', async ({ page, baseURL }) => {
 	expect(style).toContain('uppercase'); // imported styles
 	expect(style).not.toContain('#40b3ff'); // unrendered styles
 });
+
+test('http-equiv tags are removed', async ({ page }) => {
+	await page.goto('/http-equiv/cache-control');
+
+	expect(await page.textContent('h1')).toBe(
+		'the cache-control headers should be removed from this page'
+	);
+	expect(await page.innerHTML('head')).not.toContain('http-equiv="cache-control"');
+});
+
+// validation tests are skipped because amphtml-validator doesn't
+// play nicely with CI, and is also abominably slow, because
+// everything AMP-related is awful
+test.skip('prints validation errors', async ({ page, baseURL }) => {
+	await page.goto(`${baseURL}/invalid`);
+
+	const body = await page.innerHTML('body');
+
+	expect(body).toContain("Invalid URL protocol 'javascript:' for attribute 'href' in tag 'a'");
+});
+
+test.skip('throws error on encountering stylesheet links', async ({ page }) => {
+	await page.goto('/invalid/has-stylesheet');
+
+	expect(await page.textContent('body')).toContain(
+		'An AMP document cannot contain <link rel="stylesheet"> — ensure that inlineStyleThreshold is set to Infinity, and remove links from your page template and <svelte:head> elements'
+	);
+});
diff --git a/packages/kit/test/apps/basics/src/routes/assets/index.svelte b/packages/kit/test/apps/basics/src/routes/assets/index.svelte
new file mode 100644
index 000000000..0b2c43a9f
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/assets/index.svelte
@@ -0,0 +1,6 @@
+<script>
+	import logo from './logo.svg';
+</script>
+
+<h1>{logo}</h1>
+<img src={logo} alt="Svelte logo" />
diff --git a/packages/kit/test/apps/basics/src/routes/assets/logo.svg b/packages/kit/test/apps/basics/src/routes/assets/logo.svg
new file mode 100644
index 000000000..49492a83c
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/assets/logo.svg
@@ -0,0 +1,1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.1566,22.8189c-10.4-14.8851-30.94-19.2971-45.7914-9.8348L22.2825,29.6078A29.9234,29.9234,0,0,0,8.7639,49.6506a31.5136,31.5136,0,0,0,3.1076,20.2318A30.0061,30.0061,0,0,0,7.3953,81.0653a31.8886,31.8886,0,0,0,5.4473,24.1157c10.4022,14.8865,30.9423,19.2966,45.7914,9.8348L84.7167,98.3921A29.9177,29.9177,0,0,0,98.2353,78.3493,31.5263,31.5263,0,0,0,95.13,58.117a30,30,0,0,0,4.4743-11.1824,31.88,31.88,0,0,0-5.4473-24.1157" style="fill:#ff3e00"/><path d="M45.8171,106.5815A20.7182,20.7182,0,0,1,23.58,98.3389a19.1739,19.1739,0,0,1-3.2766-14.5025,18.1886,18.1886,0,0,1,.6233-2.4357l.4912-1.4978,1.3363.9815a33.6443,33.6443,0,0,0,10.203,5.0978l.9694.2941-.0893.9675a5.8474,5.8474,0,0,0,1.052,3.8781,6.2389,6.2389,0,0,0,6.6952,2.485,5.7449,5.7449,0,0,0,1.6021-.7041L69.27,76.281a5.4306,5.4306,0,0,0,2.4506-3.631,5.7948,5.7948,0,0,0-.9875-4.3712,6.2436,6.2436,0,0,0-6.6978-2.4864,5.7427,5.7427,0,0,0-1.6.7036l-9.9532,6.3449a19.0329,19.0329,0,0,1-5.2965,2.3259,20.7181,20.7181,0,0,1-22.2368-8.2427,19.1725,19.1725,0,0,1-3.2766-14.5024,17.9885,17.9885,0,0,1,8.13-12.0513L55.8833,23.7472a19.0038,19.0038,0,0,1,5.3-2.3287A20.7182,20.7182,0,0,1,83.42,29.6611a19.1739,19.1739,0,0,1,3.2766,14.5025,18.4,18.4,0,0,1-.6233,2.4357l-.4912,1.4978-1.3356-.98a33.6175,33.6175,0,0,0-10.2037-5.1l-.9694-.2942.0893-.9675a5.8588,5.8588,0,0,0-1.052-3.878,6.2389,6.2389,0,0,0-6.6952-2.485,5.7449,5.7449,0,0,0-1.6021.7041L37.73,51.719a5.4218,5.4218,0,0,0-2.4487,3.63,5.7862,5.7862,0,0,0,.9856,4.3717,6.2437,6.2437,0,0,0,6.6978,2.4864,5.7652,5.7652,0,0,0,1.602-.7041l9.9519-6.3425a18.978,18.978,0,0,1,5.2959-2.3278,20.7181,20.7181,0,0,1,22.2368,8.2427,19.1725,19.1725,0,0,1,3.2766,14.5024,17.9977,17.9977,0,0,1-8.13,12.0532L51.1167,104.2528a19.0038,19.0038,0,0,1-5.3,2.3287" style="fill:#fff"/></svg>
\ No newline at end of file
diff --git a/packages/kit/test/apps/basics/src/routes/load/url-hash.svelte b/packages/kit/test/apps/basics/src/routes/load/url-hash.svelte
new file mode 100644
index 000000000..86e1063af
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/url-hash.svelte
@@ -0,0 +1,17 @@
+<script context="module">
+	export const load = ({ url }) => {
+		const hash = url.hash;
+
+		return {
+			props: { hash }
+		};
+	};
+</script>
+
+<script>
+	export let hash;
+</script>
+
+<p id="hash">
+	{hash}
+</p>
diff --git a/packages/kit/test/apps/basics/src/routes/load/window-fetch/data.json.js b/packages/kit/test/apps/basics/src/routes/load/window-fetch/data.json.js
new file mode 100644
index 000000000..31bc26ce0
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/window-fetch/data.json.js
@@ -0,0 +1,8 @@
+/** @type {import('./data.json').RequestHandler} */
+export function get() {
+	return {
+		body: {
+			answer: 42
+		}
+	};
+}
\ No newline at end of file
diff --git a/packages/kit/test/apps/basics/src/routes/load/window-fetch/index.svelte b/packages/kit/test/apps/basics/src/routes/load/window-fetch/index.svelte
new file mode 100644
index 000000000..00ec7e8b4
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/window-fetch/index.svelte
@@ -0,0 +1,18 @@
+<script context="module">
+	/** @type {import('./index').Load} */
+	export async function load({ url }) {
+		const res = await fetch(`${url.origin}/load/window-fetch/data.json`);
+		const { answer } = await res.json();
+
+		return {
+			props: { answer }
+		};
+	}
+</script>
+
+<script>
+	/** @type {number} */
+	export let answer;
+</script>
+
+<h1>{answer}</h1>
\ No newline at end of file
diff --git a/packages/kit/test/apps/basics/src/routes/static.svelte b/packages/kit/test/apps/basics/src/routes/static.svelte
new file mode 100644
index 000000000..6e7c1e6ac
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/static.svelte
@@ -0,0 +1,1 @@
+<h1>hello</h1>
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js
index cf7086835..faae28cd8 100644
--- a/packages/kit/test/apps/basics/test/test.js
+++ b/packages/kit/test/apps/basics/test/test.js
@@ -1455,6 +1455,37 @@ test.describe.parallel('Load', () => {
 		expect(cookies.answer).toBe('42');
 		expect(cookies.doubled).toBe('84');
 	});
+
+	test('accessing url.hash from load errors and suggests using page store', async ({
+		page,
+		javaScriptEnabled
+	}) => {
+		if (javaScriptEnabled) {
+			await page.goto('/load/url-hash#please-dont-send-me-to-load');
+			expect(await page.textContent('#message')).toBe(
+				'This is your custom error page saying: "url.hash is inaccessible from load. Consider accessing hash from the page store within the script tag of your component."'
+			);
+		}
+	});
+
+	test('using window.fetch causes a warning', async ({ page, javaScriptEnabled }) => {
+		const warnings = [];
+
+		page.on('console', (msg) => {
+			if (msg.type() === 'warning') {
+				warnings.push(msg.text());
+			}
+		});
+
+		await page.goto('/load/window-fetch');
+		expect(await page.textContent('h1')).toBe('42');
+
+		if (javaScriptEnabled && process.env.DEV) {
+			expect(warnings).toContain(
+				'Loading http://localhost:3000/load/window-fetch/data.json using `window.fetch`. For best results, use the `fetch` that is passed to your `load` function: https://kit.svelte.dev/docs/loading#input-fetch'
+			);
+		}
+	});
 });
 
 test.describe.parallel('Method overrides', () => {
@@ -2361,6 +2392,11 @@ test.describe.parallel('Routing', () => {
 		expect(await page.textContent('h1')).toBe('routeId in load: routing/route-id/[x]');
 		expect(await page.textContent('h2')).toBe('routeId in store: routing/route-id/[x]');
 	});
+
+	test('serves a page that clashes with a root directory', async ({ page }) => {
+		await page.goto('/static');
+		expect(await page.textContent('h1')).toBe('hello');
+	});
 });
 
 test.describe.parallel('Session', () => {
@@ -2421,6 +2457,14 @@ test.describe.parallel('Static files', () => {
 		const response = await request.get('/static/static.json');
 		expect(response.status()).toBe(process.env.DEV ? 403 : 404);
 	});
+
+	test('Vite serves assets in src directory', async ({ page, request }) => {
+		await page.goto('/assets');
+		const path = await page.textContent('h1');
+
+		const response = await request.get(path);
+		expect(response.status()).toBe(200);
+	});
 });
 
 test.describe.parallel('Matchers', () => {