Repository: sveltejs/kit
Pull request #3565 (2022-01-27 11:52:06)

Support an async function to provide vite config
fixes https://github.com/sveltejs/kit/issues/3436 


netlify[bot]: 👷 Deploy Preview for *kit-demo* processing.


🔨 Explore the source changes: 31273df56225b2332ff841ca4fdec1f9a956031e

🔍 Inspect the deploy log: [https://app.netlify.com/sites/kit-demo/deploys/61f28768d1a3010007040dba](https://app.netlify.com/sites/kit-demo/deploys/61f28768d1a3010007040dba)

ispyinternet: At least lets me inject some async config into dev mode. Not sure about preview: https://github.com/sveltejs/kit/blob/master/packages/kit/src/core/preview/index.js#L72
benmccann: You need to run `pnpm format`
dominikg: It would be good to update the docs and config type to show that an async function can be used too. 

Please also add a changeset.

sidenote:
Got confused at first because your PR never handles the vite config as object case, but it looks like the validator turns an object into `()=>object`. I didn't expect that but makes sense to have it in a single place
Rich-Harris: This seems like a harmless change so I'm 👍 , though I'm curious what the use case is? You can use top-level await in your `svelte.config.js`, so it seems like anything enabled by this change _should_ already be possible

Patch:
diff --git a/.changeset/brown-dots-hang.md b/.changeset/brown-dots-hang.md
new file mode 100644
index 000000000..0624befa0
--- /dev/null
+++ b/.changeset/brown-dots-hang.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Move config.kit.hydrate and config.kit.router to config.kit.browser
diff --git a/.changeset/brown-impalas-travel.md b/.changeset/brown-impalas-travel.md
new file mode 100644
index 000000000..3947a3fa2
--- /dev/null
+++ b/.changeset/brown-impalas-travel.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Include service worker in manifest
diff --git a/.changeset/clever-donuts-smile.md b/.changeset/clever-donuts-smile.md
new file mode 100644
index 000000000..3a1fbc9c8
--- /dev/null
+++ b/.changeset/clever-donuts-smile.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+`svelte-kit package` gives clearer error message when svelte2tsx and typescript are not installed
diff --git a/.changeset/cuddly-ravens-carry.md b/.changeset/cuddly-ravens-carry.md
new file mode 100644
index 000000000..e78ca8b30
--- /dev/null
+++ b/.changeset/cuddly-ravens-carry.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+Try this
diff --git a/.changeset/dry-baboons-turn.md b/.changeset/dry-baboons-turn.md
new file mode 100644
index 000000000..f6156f2cd
--- /dev/null
+++ b/.changeset/dry-baboons-turn.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+Trying something
diff --git a/.changeset/eight-birds-run.md b/.changeset/eight-birds-run.md
new file mode 100644
index 000000000..23349234e
--- /dev/null
+++ b/.changeset/eight-birds-run.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/adapter-vercel': patch
+---
+
+Expose external option
diff --git a/.changeset/eight-readers-thank.md b/.changeset/eight-readers-thank.md
new file mode 100644
index 000000000..498b9f6a8
--- /dev/null
+++ b/.changeset/eight-readers-thank.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+fix handling an incoming request from HTTP/2
diff --git a/.changeset/eleven-dots-reply.md b/.changeset/eleven-dots-reply.md
new file mode 100644
index 000000000..aa71e1b79
--- /dev/null
+++ b/.changeset/eleven-dots-reply.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+`svelte-kit package` errors when lib directory does not exist
diff --git a/.changeset/hip-walls-flash.md b/.changeset/hip-walls-flash.md
new file mode 100644
index 000000000..0e7eeb381
--- /dev/null
+++ b/.changeset/hip-walls-flash.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Add kit.routes config to customise public/private modules
diff --git a/.changeset/light-pumas-heal.md b/.changeset/light-pumas-heal.md
new file mode 100644
index 000000000..d518a3dbc
--- /dev/null
+++ b/.changeset/light-pumas-heal.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Handle non-compliant ReadableStream implementations
diff --git a/.changeset/lovely-baboons-suffer.md b/.changeset/lovely-baboons-suffer.md
new file mode 100644
index 000000000..8c62ad911
--- /dev/null
+++ b/.changeset/lovely-baboons-suffer.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+Bump version
diff --git a/.changeset/modern-pillows-beam.md b/.changeset/modern-pillows-beam.md
new file mode 100644
index 000000000..7cb4479d3
--- /dev/null
+++ b/.changeset/modern-pillows-beam.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+add `prerender.createIndexFiles` option
diff --git a/.changeset/nine-kids-brake.md b/.changeset/nine-kids-brake.md
new file mode 100644
index 000000000..6d4592778
--- /dev/null
+++ b/.changeset/nine-kids-brake.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+Fix typo
diff --git a/.changeset/ninety-dolls-sniff.md b/.changeset/ninety-dolls-sniff.md
new file mode 100644
index 000000000..8c62ad911
--- /dev/null
+++ b/.changeset/ninety-dolls-sniff.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+Bump version
diff --git a/.changeset/odd-gorillas-play.md b/.changeset/odd-gorillas-play.md
new file mode 100644
index 000000000..abb7f1165
--- /dev/null
+++ b/.changeset/odd-gorillas-play.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+I think this might be it
diff --git a/.changeset/poor-lizards-punch.md b/.changeset/poor-lizards-punch.md
new file mode 100644
index 000000000..812ce635e
--- /dev/null
+++ b/.changeset/poor-lizards-punch.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[chore] refactor AMP validation
diff --git a/.changeset/pre.json b/.changeset/pre.json
index aa35bd761..e2c199524 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -10,20 +10,12 @@
     "@sveltejs/app-utils": "0.0.18",
     "create-svelte": "2.0.0-alpha.19",
     "@sveltejs/kit": "0.0.30",
-    "@sveltejs/snowpack-config": "0.0.7",
-    "test": "1",
-    "test-basics": "0.0.2",
-    "test-options": "0.0.1",
-    "hn.svelte.dev": "1.0.0",
-    "realworld.svelte.dev": "1.0.0",
-    "sandbox": "1.0.0",
-    "svelte-kit-demo": "1.0.0",
     "@sveltejs/vite-plugin-svelte": "1.0.0-next.0",
     "@sveltejs/adapter-cloudflare-workers": "0.0.1",
-    "set-env-action": "1.0.0",
-    "~TODO~": "0.0.1",
     "@sveltejs/adapter-cloudflare": "1.0.0-next.0",
     "@sveltejs/adapter-auto": "1.0.0-next",
+    "set-env-action": "1.0.0",
+    "~TODO~": "0.0.1",
     "prerendering-test-basics": "0.0.1",
     "prerendering-test-options": "0.0.1"
   },
@@ -64,6 +56,8 @@
     "bright-sheep-promise",
     "brown-bugs-arrive",
     "brown-days-agree",
+    "brown-dots-hang",
+    "brown-impalas-travel",
     "brown-jeans-cover",
     "brown-mangos-tie",
     "brown-peas-worry",
@@ -87,6 +81,7 @@
     "clean-camels-pump",
     "clean-chicken-walk",
     "clever-dolls-poke",
+    "clever-donuts-smile",
     "clever-eagles-live",
     "clever-lizards-grab",
     "clever-pillows-sing",
@@ -105,6 +100,7 @@
     "cool-swans-sleep",
     "cuddly-files-decide",
     "cuddly-pens-buy",
+    "cuddly-ravens-carry",
     "curly-pets-vanish",
     "curvy-dots-join",
     "curvy-eggs-sip",
@@ -119,6 +115,7 @@
     "cyan-seals-trade",
     "dirty-bikes-join",
     "dirty-ligers-protect",
+    "dry-baboons-turn",
     "dry-jokes-explain",
     "dry-yaks-clean",
     "dull-numbers-hunt",
@@ -130,13 +127,16 @@
     "early-news-taste",
     "early-snakes-peel",
     "early-wasps-obey",
+    "eight-birds-run",
     "eight-glasses-sparkle",
     "eight-hats-worry",
     "eight-keys-give",
+    "eight-readers-thank",
     "eighty-candles-grow",
     "eighty-carrots-heal",
     "eighty-waves-obey",
     "eleven-bugs-fail",
+    "eleven-dots-reply",
     "empty-donuts-smell",
     "empty-grapes-own",
     "empty-lamps-matter",
@@ -240,6 +240,7 @@
     "hip-elephants-judge",
     "hip-nails-burn",
     "hip-nails-taste",
+    "hip-walls-flash",
     "honest-beers-sing",
     "honest-bikes-pull",
     "honest-jars-report",
@@ -291,6 +292,7 @@
     "light-keys-mix",
     "light-olives-scream",
     "light-pumas-appear",
+    "light-pumas-heal",
     "light-roses-teach",
     "little-boats-allow",
     "little-candles-trade",
@@ -303,6 +305,7 @@
     "loud-fishes-drum",
     "loud-seals-remember",
     "lovely-apricots-fail",
+    "lovely-baboons-suffer",
     "lovely-mice-search",
     "lucky-bottles-kick",
     "lucky-phones-march",
@@ -332,6 +335,7 @@
     "modern-dryers-join",
     "modern-moose-greet",
     "modern-news-rhyme",
+    "modern-pillows-beam",
     "modern-sloths-greet",
     "moody-laws-draw",
     "moody-queens-sell",
@@ -360,13 +364,16 @@
     "nine-actors-whisper",
     "nine-beans-rhyme",
     "nine-dots-occur",
+    "nine-kids-brake",
     "nine-rats-heal",
     "nine-starfishes-destroy",
     "nine-tomatoes-poke",
+    "ninety-dolls-sniff",
     "ninety-games-flow",
     "ninety-singers-invite",
     "odd-coins-lie",
     "odd-foxes-sing",
+    "odd-gorillas-play",
     "odd-islands-heal",
     "odd-ligers-march",
     "odd-ligers-swim",
@@ -399,6 +406,7 @@
     "poor-bobcats-unite",
     "poor-cameras-kick",
     "poor-goats-cheer",
+    "poor-lizards-punch",
     "poor-snakes-explode",
     "poor-terms-explain",
     "poor-years-hear",
@@ -415,6 +423,7 @@
     "pretty-mayflies-tickle",
     "pretty-snakes-burn",
     "proud-bananas-love",
+    "proud-buses-hug",
     "proud-hairs-chew",
     "proud-needles-shake",
     "proud-numbers-promise",
@@ -452,6 +461,7 @@
     "rotten-gorillas-sort",
     "rotten-singers-taste",
     "rotten-tips-teach",
+    "rotten-wasps-beam",
     "rude-balloons-return",
     "rude-olives-wink",
     "rude-starfishes-press",
@@ -465,6 +475,7 @@
     "serious-books-push",
     "serious-dolphins-approve",
     "serious-fireants-drum",
+    "serious-horses-rule",
     "serious-vans-arrive",
     "seven-avocados-hug",
     "seven-bags-sniff",
@@ -472,6 +483,7 @@
     "seven-impalas-march",
     "seven-jars-argue",
     "seven-planes-fly",
+    "shaggy-donkeys-run",
     "shaggy-elephants-sell",
     "shaggy-peaches-fry",
     "sharp-days-exercise",
@@ -508,6 +520,7 @@
     "sixty-pianos-fix",
     "sixty-rice-cough",
     "sixty-terms-decide",
+    "slimy-cougars-double",
     "slow-buses-beam",
     "slow-fans-own",
     "slow-suns-reply",
@@ -605,6 +618,7 @@
     "thin-coins-move",
     "thin-grapes-dance",
     "thin-planets-obey",
+    "thin-swans-cheat",
     "thin-trains-own",
     "thirty-frogs-mix",
     "thirty-spies-sparkle",
@@ -647,9 +661,11 @@
     "twelve-onions-burn",
     "twenty-dryers-hope",
     "twenty-hairs-shave",
+    "twenty-papayas-compare",
     "twenty-turkeys-promise",
     "two-buttons-eat",
     "two-crabs-tease",
+    "two-moles-brush",
     "two-students-melt",
     "two-ties-begin",
     "two-tigers-cry",
diff --git a/.changeset/proud-buses-hug.md b/.changeset/proud-buses-hug.md
new file mode 100644
index 000000000..98213f1c8
--- /dev/null
+++ b/.changeset/proud-buses-hug.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Decode fetched resources before checking against manifest when prerendering
diff --git a/.changeset/rotten-wasps-beam.md b/.changeset/rotten-wasps-beam.md
new file mode 100644
index 000000000..341d4db0d
--- /dev/null
+++ b/.changeset/rotten-wasps-beam.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+And again
diff --git a/.changeset/serious-horses-rule.md b/.changeset/serious-horses-rule.md
new file mode 100644
index 000000000..9932eb971
--- /dev/null
+++ b/.changeset/serious-horses-rule.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Breaking: remove -H and (conflicting) -h shortcuts from CLI
diff --git a/.changeset/shaggy-donkeys-run.md b/.changeset/shaggy-donkeys-run.md
new file mode 100644
index 000000000..12808dff1
--- /dev/null
+++ b/.changeset/shaggy-donkeys-run.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+Another meaningless change
diff --git a/.changeset/slimy-cougars-double.md b/.changeset/slimy-cougars-double.md
new file mode 100644
index 000000000..a7d2f5e66
--- /dev/null
+++ b/.changeset/slimy-cougars-double.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+remove nonexistent `url` store from `$app/stores` ambient types
diff --git a/.changeset/sour-shirts-camp.md b/.changeset/sour-shirts-camp.md
new file mode 100644
index 000000000..8bdc5a6c2
--- /dev/null
+++ b/.changeset/sour-shirts-camp.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Allow config.kit.vite to be an async function
diff --git a/.changeset/thin-swans-cheat.md b/.changeset/thin-swans-cheat.md
new file mode 100644
index 000000000..12bd412ab
--- /dev/null
+++ b/.changeset/thin-swans-cheat.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+Tweak README
diff --git a/.changeset/twenty-papayas-compare.md b/.changeset/twenty-papayas-compare.md
new file mode 100644
index 000000000..341d4db0d
--- /dev/null
+++ b/.changeset/twenty-papayas-compare.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+And again
diff --git a/.changeset/two-moles-brush.md b/.changeset/two-moles-brush.md
new file mode 100644
index 000000000..60fa6e213
--- /dev/null
+++ b/.changeset/two-moles-brush.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+typo
diff --git a/.github/actions/env/package.json b/.github/actions/env/package.json
index 4541861bc..ebd47ea84 100644
--- a/.github/actions/env/package.json
+++ b/.github/actions/env/package.json
@@ -3,6 +3,7 @@
 	"version": "1.0.0",
 	"description": "Action to set environment",
 	"main": "action/main.js",
+	"private": true,
 	"scripts": {
 		"build": "rollup -c"
 	},
diff --git a/.github/workflows/update_template.yml b/.github/workflows/update_template.yml
deleted file mode 100644
index dcca025e1..000000000
--- a/.github/workflows/update_template.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-name: Update Template
-
-on:
-  workflow_run:
-    workflows: [Release]
-    types:
-      - completed
-  workflow_dispatch:
-
-jobs:
-  release:
-    # prevents this action from running on forks
-    if: github.repository == 'sveltejs/kit'
-    name: build
-    runs-on: ubuntu-latest
-
-    steps:
-      - name: Check out kit repository
-        uses: actions/checkout@v2
-        with:
-          path: kit
-
-      - name: Check out template repository
-        uses: actions/checkout@v2
-        with:
-          ssh-key: "${{ secrets.UPDATE_TEMPLATE_SSH_KEY }}"
-          repository: sveltejs/kit-template-default
-          path: template
-
-      - uses: pnpm/action-setup@v2.0.1
-        with:
-          version: 6.23.2
-
-      - name: Setup Node.js
-        uses: actions/setup-node@v2
-        with:
-          node-version: 16.x
-          cache: pnpm
-          cache-dependency-path: ${{ github.workspace }}/kit/pnpm-lock.yaml
-
-      - name: Build kit
-        working-directory: ${{ github.workspace }}/kit
-        run: |
-          pnpm install --frozen-lockfile
-          pnpm build
-
-      - name: Write template
-        working-directory: ${{ github.workspace }}/kit/packages/create-svelte
-        run: node ./scripts/update-template-repo-contents.js ${{ github.workspace }}/template
-
-      - name: Publish
-        working-directory: ${{ github.workspace }}/template
-        run: |
-          touch "generated-$(date +%s)"
-          git config user.email "noreply@svelte.dev"
-          git config user.name "[bot]"
-          git add -A
-          git commit -m "version $npm_package_version"
-          git push -u origin main -f
diff --git a/documentation/docs/01-routing.md b/documentation/docs/01-routing.md
index 344b71c2e..8af291a24 100644
--- a/documentation/docs/01-routing.md
+++ b/documentation/docs/01-routing.md
@@ -175,7 +175,7 @@ export default {
 
 ### Private modules
 
-A filename that has a segment with a leading underscore, such as `src/routes/foo/_Private.svelte` or `src/routes/bar/_utils/cool-util.js`, is hidden from the router, but can be imported by files that are not.
+Files and directories with a leading `_` or `.` (other than [`.well-known`](https://en.wikipedia.org/wiki/Well-known_URI)) are private by default, meaning that they do not create routes (but can be imported by files that do). You can configure which modules are considered public or private with the [`routes`](#configuration-routes) configuration.
 
 ### Advanced
 
diff --git a/documentation/docs/05-modules.md b/documentation/docs/05-modules.md
index 72b606884..63ee39601 100644
--- a/documentation/docs/05-modules.md
+++ b/documentation/docs/05-modules.md
@@ -95,13 +95,23 @@ This module provides a helper function to sequence multiple `handle` calls.
 import { sequence } from '@sveltejs/kit/hooks';
 
 async function first({ event, resolve }) {
-	console.log('first');
-	return await resolve(event);
+	console.log('first pre-processing');
+	const result = await resolve(event);
+	console.log('first post-processing');
+	return result;
 }
 async function second({ event, resolve }) {
-	console.log('second');
-	return await resolve(event);
+	console.log('second pre-processing');
+	const result = await resolve(event);
+	console.log('second post-processing');
+	return result;
 }
 
 export const handle = sequence(first, second);
 ```
+
+The example above would print:
+>first pre-processing
+>second pre-processing
+>second post-processing
+>first post-processing
diff --git a/documentation/docs/11-page-options.md b/documentation/docs/11-page-options.md
index cb70b5c17..3beeea49c 100644
--- a/documentation/docs/11-page-options.md
+++ b/documentation/docs/11-page-options.md
@@ -12,7 +12,7 @@ If both are specified, per-page settings override per-app settings in case of co
 
 SvelteKit includes a [client-side router](#appendix-routing) that intercepts navigations (from the user clicking on links, or interacting with the back/forward buttons) and updates the page contents, rather than letting the browser handle the navigation by reloading.
 
-In certain circumstances you might need to disable [client-side routing](#appendix-routing) with the app-wide [`router` config option](#configuration-router) or the page-level `router` export:
+In certain circumstances you might need to disable [client-side routing](#appendix-routing) with the app-wide [`browser.router` config option](#configuration-browser) or the page-level `router` export:
 
 ```html
 <script context="module">
@@ -24,7 +24,7 @@ Note that this will disable client-side routing for any navigation from this pag
 
 ### hydrate
 
-Ordinarily, SvelteKit [hydrates](#appendix-hydration) your server-rendered HTML into an interactive page. Some pages don't require JavaScript at all — many blog posts and 'about' pages fall into this category. In these cases you can skip hydration when the app boots up with the app-wide [`hydrate` config option](#configuration-hydrate) or the page-level `hydrate` export:
+Ordinarily, SvelteKit [hydrates](#appendix-hydration) your server-rendered HTML into an interactive page. Some pages don't require JavaScript at all — many blog posts and 'about' pages fall into this category. In these cases you can skip hydration when the app boots up with the app-wide [`browser.hydrate` config option](#configuration-browser) or the page-level `hydrate` export:
 
 ```html
 <script context="module">
diff --git a/documentation/docs/14-configuration.md b/documentation/docs/14-configuration.md
index 95e3b2f42..a6470eb0f 100644
--- a/documentation/docs/14-configuration.md
+++ b/documentation/docs/14-configuration.md
@@ -17,6 +17,10 @@ const config = {
 		adapter: null,
 		amp: false,
 		appDir: '_app',
+		browser: {
+			hydrate: true,
+			router: true
+		},
 		csp: {
 			mode: 'auto',
 			directives: {
@@ -33,7 +37,6 @@ const config = {
 			template: 'src/app.html'
 		},
 		floc: false,
-		hydrate: true,
 		inlineStyleThreshold: 0,
 		methodOverride: {
 			parameter: '_method',
@@ -53,11 +56,12 @@ const config = {
 		prerender: {
 			concurrency: 1,
 			crawl: true,
+			createIndexFiles: true,
 			enabled: true,
 			entries: ['*'],
 			onError: 'fail'
 		},
-		router: true,
+		routes: (filepath) => !/(?:(?:^_|\/_)|(?:^\.|\/\.)(?!well-known))/.test(filepath),
 		serviceWorker: {
 			register: true,
 			files: (filepath) => !/\.DS_STORE/.test(filepath)
@@ -89,6 +93,13 @@ Enable [AMP](#amp) mode.
 
 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 `/`.
 
+### browser
+
+An object containing zero or more of the following `boolean` values:
+
+- `hydrate` — whether to [hydrate](#page-options-hydrate) the server-rendered HTML with a client-side app. (It's rare that you would set this to `false` on an app-wide basis.)
+- `router` — enables or disables the client-side [router](#page-options-router) app-wide.
+
 ### csp
 
 An object containing zero or more of the following values:
@@ -135,10 +146,6 @@ Permissions-Policy: interest-cohort=()
 
 > This only applies to server-rendered responses — headers for prerendered pages (e.g. created with [adapter-static](https://github.com/sveltejs/kit/tree/master/packages/adapter-static)) are determined by the hosting platform.
 
-### hydrate
-
-Whether to [hydrate](#page-options-hydrate) the server-rendered HTML with a client-side app. (It's rare that you would set this to `false` on an app-wide basis.)
-
 ### inlineStyleThreshold
 
 Inline CSS inside a `<style>` block at the head of the HTML. This option is a number that specifies the maximum length of a CSS file to be inlined. All CSS files needed for the page and smaller than this value are merged and inlined in a `<style>` block.
@@ -193,6 +200,7 @@ See [Prerendering](#page-options-prerender). An object containing zero or more o
 
 - `concurrency` — how many pages can be prerendered simultaneously. JS is single-threaded, but in cases where prerendering performance is network-bound (for example loading content from a remote CMS) this can speed things up by processing other tasks while waiting on the network response
 - `crawl` — determines whether SvelteKit should find pages to prerender by following links from the seed page(s)
+- `createIndexFiles` - if set to `false`, will render `about.html` instead of `about/index.html`
 - `enabled` — set to `false` to disable prerendering altogether
 - `entries` — an array of pages to prerender, or start crawling from (if `crawl: true`). The `*` string includes all non-dynamic routes (i.e. pages with no `[parameters]` )
 - `onError`
@@ -220,9 +228,9 @@ See [Prerendering](#page-options-prerender). An object containing zero or more o
     };
     ```
 
-### router
+### routes
 
-Enables or disables the client-side [router](#page-options-router) app-wide.
+A `(filepath: string) => boolean` function that determines which files create routes and which are treated as [private modules](#routing-private-modules).
 
 ### serviceWorker
 
diff --git a/documentation/faq/70-packages.md b/documentation/faq/70-packages.md
index 9281f3e62..a50213276 100644
--- a/documentation/faq/70-packages.md
+++ b/documentation/faq/70-packages.md
@@ -2,10 +2,14 @@
 question: How do I fix the error I'm getting trying to include a package?
 ---
 
-SSR in Vite is not yet stable. Libraries work best with Vite when they distribute both CJS and ESM in their package and you may wish to work with library authors to make this happen.
+Vite's SSR support has become fairly stable since Vite 2.7. Most issues related to including a library are due to incorrect packaging.
 
-Svelte components must be written entirely in ESM. It is encouraged to make sure the dependencies of external Svelte components provide an ESM version. However, in order to handle CJS dependencies [`vite-plugin-svelte` will look for any CJS dependencies of external Svelte components](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#what-is-going-on-with-vite-and-pre-bundling-dependencies) and ask Vite to pre-bundle them by automatically adding them to Vite's `optimizeDeps.include` which will use `esbuild` to convert them to ESM.
+Libraries work best with Vite when they distribute an ESM version and you may wish to suggest this to library authors. Here are a few things to keep in mind when checking if a library is packaged correctly:
+- `exports` takes precedence over the other entry point fields such as `main` and `module`. Adding an `exports` field may not be backwards-compatible as it prevents deep imports.
+- ESM files should end with `.mjs` unless `"type": "module"` is set in which any case CommonJS files should end with `.cjs`.
+- `main` should be defined if `exports` is not. It should be either a CommonJS or ESM file and adhere to the previous bullet. If a `module` field is defined, it should refer to an ESM file.
+- Svelte components should be distributed entirely as ESM and have a `svelte` field defining the entry point.
 
-A side effect of the approach above is that it takes longer to load the initial page. If this becomes noticable, try setting [experimental.prebundleSvelteLibraries: true](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md#prebundlesveltelibraries) in `svelte.config.js`. Note that this option is experimental.
+It is encouraged to make sure the dependencies of external Svelte components provide an ESM version. However, in order to handle CommonJS dependencies [`vite-plugin-svelte` will look for any CJS dependencies of external Svelte components](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#what-is-going-on-with-vite-and-pre-bundling-dependencies) and ask Vite to pre-bundle them by automatically adding them to Vite's `optimizeDeps.include` which will use `esbuild` to convert them to ESM. A side effect of this approach is that it takes longer to load the initial page. If this becomes noticable, try setting [experimental.prebundleSvelteLibraries: true](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md#prebundlesveltelibraries) in `svelte.config.js`. Note that this option is experimental.
 
 If you are still encountering issues we recommend checking [the list of known Vite issues most commonly affecting SvelteKit users](https://github.com/sveltejs/kit/issues/2086) and searching both [the Vite issue tracker](https://github.com/vitejs/vite/issues) and the issue tracker of the library in question. Sometimes issues can be worked around by fiddling with the [`optimizeDeps`](https://vitejs.dev/config/#dep-optimization-options) or [`ssr`](https://vitejs.dev/config/#ssr-options) config values.
diff --git a/documentation/migrating/04-pages.md b/documentation/migrating/04-pages.md
index f06db769e..8986d513f 100644
--- a/documentation/migrating/04-pages.md
+++ b/documentation/migrating/04-pages.md
@@ -4,7 +4,7 @@ title: Pages and layouts
 
 ### Renamed files
 
-Your custom error page component should be renamed from `_error.svelte` to `__error.svelte`. Any `_layout.svelte` files should likewise be renamed `__layout.svelte`. The double underscore prefix is reserved for SvelteKit; your own private modules are still denoted with a single `_` prefix.
+Your custom error page component should be renamed from `_error.svelte` to `__error.svelte`. Any `_layout.svelte` files should likewise be renamed `__layout.svelte`. The double underscore prefix is reserved for SvelteKit; your own [private modules](#routing-private-modules) are still denoted with a single `_` prefix (configurable via [`routes`](docs#configuration-routes) config).
 
 ### Imports
 
diff --git a/package.json b/package.json
index 8abc7d377..d5bd66957 100644
--- a/package.json
+++ b/package.json
@@ -9,9 +9,8 @@
 		"check": "pnpm -r check",
 		"lint": "pnpm -r lint",
 		"format": "pnpm -r format",
-		"release": "pnpm publish --tag=next --filter=\"@sveltejs/*\" --filter=\"create-svelte\"",
-		"preview:docs": "action-deploy-docs --project=kit",
-		"changeset:version": "changeset version && pnpm i --lockfile-only"
+		"release": "changeset publish",
+		"preview:docs": "action-deploy-docs --project=kit"
 	},
 	"repository": {
 		"type": "git",
@@ -33,7 +32,7 @@
 		"@typescript-eslint/eslint-plugin": "^5.5.0",
 		"@typescript-eslint/parser": "^5.5.0",
 		"action-deploy-docs": "github:sveltejs/action-deploy-docs#main",
-		"dotenv": "^14.0.0",
+		"dotenv": "^15.0.0",
 		"eslint": "^8.3.0",
 		"eslint-plugin-import": "^2.25.3",
 		"eslint-plugin-svelte3": "^3.2.1",
diff --git a/packages/adapter-auto/CHANGELOG.md b/packages/adapter-auto/CHANGELOG.md
index 0cbb58f97..8ac84722c 100644
--- a/packages/adapter-auto/CHANGELOG.md
+++ b/packages/adapter-auto/CHANGELOG.md
@@ -1,5 +1,14 @@
 # @sveltejs/adapter-auto
 
+## 1.0.0-next.18
+
+### Patch Changes
+
+- Updated dependencies [[`48f48162`](https://github.com/sveltejs/kit/commit/48f481626908377aa156a4cc4e177d2d7d663c81)]:
+  - @sveltejs/adapter-vercel@1.0.0-next.40
+  - @sveltejs/adapter-cloudflare@1.0.0-next.9
+  - @sveltejs/adapter-netlify@1.0.0-next.44
+
 ## 1.0.0-next.17
 
 ### Patch Changes
diff --git a/packages/adapter-auto/package.json b/packages/adapter-auto/package.json
index e681afd0d..d7783e97c 100644
--- a/packages/adapter-auto/package.json
+++ b/packages/adapter-auto/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/adapter-auto",
-	"version": "1.0.0-next.17",
+	"version": "1.0.0-next.18",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/adapter-cloudflare/README.md b/packages/adapter-cloudflare/README.md
index 1a73dbf3e..dffc3709a 100644
--- a/packages/adapter-cloudflare/README.md
+++ b/packages/adapter-cloudflare/README.md
@@ -2,6 +2,8 @@
 
 [Adapter](https://kit.svelte.dev/docs#adapters) for building SvelteKit applications on [Cloudflare Pages](https://developers.cloudflare.com/pages/) with [Workers integration](https://developers.cloudflare.com/pages/platform/functions).
 
+If you're using [adapter-auto](../adapter-auto), you don't need to install this — it's already included.
+
 _**Comparisons**_
 
 - `adapter-cloudflare` – supports all SvelteKit features; builds for
diff --git a/packages/adapter-netlify/README.md b/packages/adapter-netlify/README.md
index 8cbcae508..8fa49dc23 100644
--- a/packages/adapter-netlify/README.md
+++ b/packages/adapter-netlify/README.md
@@ -1,6 +1,8 @@
 # adapter-netlify
 
-Adapter for Svelte apps that creates a Netlify app, using serverless functions for dynamically generating pages.
+A SvelteKit adapter that creates a Netlify app.
+
+If you're using [adapter-auto](../adapter-auto), you don't need to install this unless you need to specify Netlify-specific options, since it's already included.
 
 ## Installation
 
diff --git a/packages/adapter-vercel/CHANGELOG.md b/packages/adapter-vercel/CHANGELOG.md
index a83cef3e5..bba3d8dc1 100644
--- a/packages/adapter-vercel/CHANGELOG.md
+++ b/packages/adapter-vercel/CHANGELOG.md
@@ -1,5 +1,11 @@
 # @sveltejs/adapter-vercel
 
+## 1.0.0-next.40
+
+### Patch Changes
+
+- Expose external option ([#3614](https://github.com/sveltejs/kit/pull/3614))
+
 ## 1.0.0-next.39
 
 ### Patch Changes
diff --git a/packages/adapter-vercel/README.md b/packages/adapter-vercel/README.md
index 9fba0c626..9717b67f1 100644
--- a/packages/adapter-vercel/README.md
+++ b/packages/adapter-vercel/README.md
@@ -1,6 +1,8 @@
 # adapter-vercel
 
-Adapter for Svelte apps that creates a Vercel app, using a function for dynamic server rendering.
+A SvelteKit adapter that creates a Vercel app.
+
+If you're using [adapter-auto](../adapter-auto), you don't need to install this unless you need to specify Vercel-specific options, since it's already included.
 
 ## Usage
 
@@ -14,11 +16,17 @@ import vercel from '@sveltejs/adapter-vercel';
 export default {
 	kit: {
 		...
-		adapter: vercel()
+		adapter: vercel(options)
 	}
 };
 ```
 
+## Options
+
+You can pass an `options` argument, if necessary, with the following:
+
+- `external` — an array of dependencies that [esbuild](https://esbuild.github.io/api/#external) should treat as external
+
 ## Changelog
 
 [The Changelog for this package is available on GitHub](https://github.com/sveltejs/kit/blob/master/packages/adapter-vercel/CHANGELOG.md).
diff --git a/packages/adapter-vercel/index.d.ts b/packages/adapter-vercel/index.d.ts
index 62aee0f1e..25e220d37 100644
--- a/packages/adapter-vercel/index.d.ts
+++ b/packages/adapter-vercel/index.d.ts
@@ -1,4 +1,8 @@
 import { Adapter } from '@sveltejs/kit';
 
-declare function plugin(): Adapter;
+type Options = {
+	external?: string[];
+};
+
+declare function plugin(options?: Options): Adapter;
 export = plugin;
diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js
index 5c5a38f23..269f88e59 100644
--- a/packages/adapter-vercel/index.js
+++ b/packages/adapter-vercel/index.js
@@ -6,7 +6,7 @@ import esbuild from 'esbuild';
 const dir = '.vercel_build_output';
 
 /** @type {import('.')} **/
-export default function () {
+export default function ({ external = [] } = {}) {
 	return {
 		name: '@sveltejs/adapter-vercel',
 
@@ -52,7 +52,8 @@ export default function () {
 				outfile: `${dirs.lambda}/index.js`,
 				target: 'node14',
 				bundle: true,
-				platform: 'node'
+				platform: 'node',
+				external
 			});
 
 			writeFileSync(`${dirs.lambda}/package.json`, JSON.stringify({ type: 'commonjs' }));
diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json
index a8cba5256..e1f30f69e 100644
--- a/packages/adapter-vercel/package.json
+++ b/packages/adapter-vercel/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/adapter-vercel",
-	"version": "1.0.0-next.39",
+	"version": "1.0.0-next.40",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/create-svelte/CHANGELOG.md b/packages/create-svelte/CHANGELOG.md
index 15f26d6f5..5b298e54b 100644
--- a/packages/create-svelte/CHANGELOG.md
+++ b/packages/create-svelte/CHANGELOG.md
@@ -1,5 +1,71 @@
 # create-svelte
 
+## 2.0.0-next.113
+
+### Patch Changes
+
+- Bump version ([#3610](https://github.com/sveltejs/kit/pull/3610))
+
+## 2.0.0-next.112
+
+### Patch Changes
+
+- Try this ([#3608](https://github.com/sveltejs/kit/pull/3608))
+
+## 2.0.0-next.111
+
+### Patch Changes
+
+- I think this might be it ([#3606](https://github.com/sveltejs/kit/pull/3606))
+
+## 2.0.0-next.110
+
+### Patch Changes
+
+- And again ([#3604](https://github.com/sveltejs/kit/pull/3604))
+
+## 2.0.0-next.109
+
+### Patch Changes
+
+- Bump version ([#3602](https://github.com/sveltejs/kit/pull/3602))
+
+## 2.0.0-next.108
+
+### Patch Changes
+
+- And again ([#3600](https://github.com/sveltejs/kit/pull/3600))
+
+## 2.0.0-next.107
+
+### Patch Changes
+
+- Trying something ([#3598](https://github.com/sveltejs/kit/pull/3598))
+
+## 2.0.0-next.105
+
+### Patch Changes
+
+- Another meaningless change ([#3596](https://github.com/sveltejs/kit/pull/3596))
+
+## 2.0.0-next.104
+
+### Patch Changes
+
+- typo ([#3593](https://github.com/sveltejs/kit/pull/3593))
+
+## 2.0.0-next.103
+
+### Patch Changes
+
+- Tweak README ([#3591](https://github.com/sveltejs/kit/pull/3591))
+
+## 2.0.0-next.102
+
+### Patch Changes
+
+- Fix typo ([#3583](https://github.com/sveltejs/kit/pull/3583))
+
 ## 2.0.0-next.101
 
 ### Patch Changes
diff --git a/packages/create-svelte/package.json b/packages/create-svelte/package.json
index fd4471339..c067cc83b 100644
--- a/packages/create-svelte/package.json
+++ b/packages/create-svelte/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "create-svelte",
-	"version": "2.0.0-next.101",
+	"version": "2.0.0-next.113",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
@@ -33,7 +33,8 @@
 		"lint": "eslint --ignore-path .gitignore --ignore-path ../../.gitignore \"./*.js\" && npm run check-format",
 		"format": "npm run check-format -- --write",
 		"check-format": "prettier --check . --config ../../.prettierrc --ignore-path ../../.gitignore --ignore-path .gitignore --plugin prettier-plugin-svelte --plugin-search-dir=.",
-		"prepublishOnly": "npm run build"
+		"prepublishOnly": "npm run build",
+		"postpublish": "echo \"Updating template repo\" && bash ./scripts/update-template-repo.sh"
 	},
 	"files": [
 		"index.js",
diff --git a/packages/create-svelte/scripts/update-template-repo.sh b/packages/create-svelte/scripts/update-template-repo.sh
new file mode 100755
index 000000000..f3997e29e
--- /dev/null
+++ b/packages/create-svelte/scripts/update-template-repo.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+get_abs_filename() {
+  # $1 : relative filename
+  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+}
+
+DIR=$(get_abs_filename $(dirname "$0"))
+TMP=$(get_abs_filename "$DIR/../node_modules/.tmp")
+
+mkdir -p $TMP
+cd $TMP
+
+if [ "$CI" ]; then
+	(umask 0077; echo "$UPDATE_TEMPLATE_SSH_KEY" > ~/ssh_key;)
+	export GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=accept-new -i ~/ssh_key'
+fi
+
+# clone the template repo
+rm -rf kit-template-default
+git clone --depth 1 --single-branch --branch main git@github.com:sveltejs/kit-template-default.git kit-template-default
+
+# empty out the repo
+cd kit-template-default
+node $DIR/update-template-repo-contents.js $TMP/kit-template-default
+
+if [ "$CI" ]; then
+	git config user.email 'noreply@svelte.dev'
+	git config user.name '[bot]'
+fi
+
+# commit the new files
+git add -A
+git commit -m "version $npm_package_version"
+
+git push git@github.com:sveltejs/kit-template-default.git main -f
\ No newline at end of file
diff --git a/packages/create-svelte/shared/README.md b/packages/create-svelte/shared/README.md
index 82510ca06..40fe42404 100644
--- a/packages/create-svelte/shared/README.md
+++ b/packages/create-svelte/shared/README.md
@@ -1,6 +1,6 @@
 # create-svelte
 
-Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte);
+Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
 
 ## Creating a project
 
@@ -29,10 +29,12 @@ npm run dev -- --open
 
 ## Building
 
-Before creating a production version of your app, install an [adapter](https://kit.svelte.dev/docs#adapters) for your target environment. Then:
+To create a production version of your app:
 
 ```bash
 npm run build
 ```
 
-> You can preview the built app with `npm run preview`, regardless of whether you installed an adapter. This should _not_ be used to serve your app in production.
+You can preview the production build with `npm run preview`.
+
+> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs#adapters) for your target environment.
diff --git a/packages/create-svelte/templates/default/package.json b/packages/create-svelte/templates/default/package.json
index ae51f365e..1aa680fa5 100644
--- a/packages/create-svelte/templates/default/package.json
+++ b/packages/create-svelte/templates/default/package.json
@@ -1,6 +1,7 @@
 {
 	"name": "~TODO~",
 	"version": "0.0.1",
+	"private": true,
 	"scripts": {
 		"dev": "svelte-kit dev",
 		"build": "svelte-kit build --verbose",
diff --git a/packages/create-svelte/templates/default/package.template.json b/packages/create-svelte/templates/default/package.template.json
index cd0485149..c7cdcf5f7 100644
--- a/packages/create-svelte/templates/default/package.template.json
+++ b/packages/create-svelte/templates/default/package.template.json
@@ -10,7 +10,7 @@
 	"devDependencies": {
 		"@sveltejs/adapter-auto": "next",
 		"@sveltejs/kit": "next",
-		"svelte": "^3.44.0"
+		"svelte": "^3.46.0"
 	},
 	"type": "module",
 	"dependencies": {
diff --git a/packages/kit/.gitignore b/packages/kit/.gitignore
index 4d29456c8..49c469e4d 100644
--- a/packages/kit/.gitignore
+++ b/packages/kit/.gitignore
@@ -5,5 +5,5 @@
 /client/**/*.d.ts
 /test/**/.svelte-kit
 /test/**/build
-!/src/core/adapt/test/fixtures/*/.svelte-kit
+!/src/core/adapt/fixtures/*/.svelte-kit
 !/test/node_modules
\ No newline at end of file
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index 4719d510a..5f691726d 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,57 @@
 # @sveltejs/kit
 
+## 1.0.0-next.252
+
+### Patch Changes
+
+- remove nonexistent `url` store from `$app/stores` ambient types ([#3640](https://github.com/sveltejs/kit/pull/3640))
+
+## 1.0.0-next.251
+
+### Patch Changes
+
+- Handle non-compliant ReadableStream implementations ([#3624](https://github.com/sveltejs/kit/pull/3624))
+
+## 1.0.0-next.250
+
+### Patch Changes
+
+- Move config.kit.hydrate and config.kit.router to config.kit.browser ([#3578](https://github.com/sveltejs/kit/pull/3578))
+
+* add `prerender.createIndexFiles` option ([#2632](https://github.com/sveltejs/kit/pull/2632))
+
+## 1.0.0-next.249
+
+### Patch Changes
+
+- Include service worker in manifest ([#3570](https://github.com/sveltejs/kit/pull/3570))
+
+* Add kit.routes config to customise public/private modules ([#3576](https://github.com/sveltejs/kit/pull/3576))
+
+## 1.0.0-next.248
+
+### Patch Changes
+
+- Decode fetched resources before checking against manifest when prerendering ([#3571](https://github.com/sveltejs/kit/pull/3571))
+
+* Breaking: remove -H and (conflicting) -h shortcuts from CLI ([#3573](https://github.com/sveltejs/kit/pull/3573))
+
+## 1.0.0-next.247
+
+### Patch Changes
+
+- fix handling an incoming request from HTTP/2 ([#3572](https://github.com/sveltejs/kit/pull/3572))
+
+## 1.0.0-next.246
+
+### Patch Changes
+
+- `svelte-kit package` gives clearer error message when svelte2tsx and typescript are not installed ([#3562](https://github.com/sveltejs/kit/pull/3562))
+
+* `svelte-kit package` errors when lib directory does not exist ([#3562](https://github.com/sveltejs/kit/pull/3562))
+
+- [chore] refactor AMP validation ([#3554](https://github.com/sveltejs/kit/pull/3554))
+
 ## 1.0.0-next.245
 
 ### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index 710483b73..512860651 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.0.0-next.245",
+	"version": "1.0.0-next.252",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
@@ -40,7 +40,7 @@
 		"svelte": "^3.44.2",
 		"svelte-check": "^2.2.10",
 		"svelte-preprocess": "^4.9.8",
-		"svelte2tsx": "~0.4.10",
+		"svelte2tsx": "~0.5.0",
 		"tiny-glob": "^0.2.9",
 		"uvu": "^0.5.2"
 	},
diff --git a/packages/kit/src/cli.js b/packages/kit/src/cli.js
index cf5e532e3..44284332c 100755
--- a/packages/kit/src/cli.js
+++ b/packages/kit/src/cli.js
@@ -45,11 +45,14 @@ prog
 	.command('dev')
 	.describe('Start a development server')
 	.option('-p, --port', 'Port')
-	.option('-h, --host', 'Host (only use this on trusted networks)')
-	.option('-H, --https', 'Use self-signed HTTPS certificate')
 	.option('-o, --open', 'Open a browser tab')
-	.action(async ({ port, host, https, open }) => {
+	.option('--host', 'Host (only use this on trusted networks)')
+	.option('--https', 'Use self-signed HTTPS certificate')
+	.option('-H', 'no longer supported, use --https instead') // TODO remove for 1.0
+	.action(async ({ port, host, https, open, H }) => {
 		try {
+			if (H) throw new Error('-H is no longer supported — use --https instead');
+
 			process.env.NODE_ENV = process.env.NODE_ENV || 'development';
 			const config = await load_config();
 
@@ -118,11 +121,14 @@ prog
 	.command('preview')
 	.describe('Serve an already-built app')
 	.option('-p, --port', 'Port', 3000)
-	.option('-h, --host', 'Host (only use this on trusted networks)', 'localhost')
-	.option('-H, --https', 'Use self-signed HTTPS certificate', false)
 	.option('-o, --open', 'Open a browser tab', false)
-	.action(async ({ port, host, https, open }) => {
+	.option('--host', 'Host (only use this on trusted networks)', 'localhost')
+	.option('--https', 'Use self-signed HTTPS certificate', false)
+	.option('-H', 'no longer supported, use --https instead') // TODO remove for 1.0
+	.action(async ({ port, host, https, open, H }) => {
 		try {
+			if (H) throw new Error('-H is no longer supported — use --https instead');
+
 			await check_port(port);
 
 			process.env.NODE_ENV = process.env.NODE_ENV || 'production';
diff --git a/packages/kit/src/core/adapt/builder.spec.js b/packages/kit/src/core/adapt/builder.spec.js
new file mode 100644
index 000000000..971f6339b
--- /dev/null
+++ b/packages/kit/src/core/adapt/builder.spec.js
@@ -0,0 +1,67 @@
+import { rmSync } from 'fs';
+import { join } from 'path';
+import { test } from 'uvu';
+import * as assert from 'uvu/assert';
+import glob from 'tiny-glob/sync.js';
+import { create_builder } from './builder.js';
+import { fileURLToPath } from 'url';
+import { SVELTE_KIT } from '../constants.js';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = join(__filename, '..');
+
+test('copy files', () => {
+	const cwd = join(__dirname, 'fixtures/basic');
+
+	/** @type {import('types/config').Config} */
+	const mocked = {
+		extensions: ['.svelte'],
+		kit: {
+			appDir: '_app',
+			files: {
+				assets: join(__dirname, 'fixtures/basic/static')
+			}
+		}
+	};
+
+	const builder = create_builder({
+		cwd,
+		config: /** @type {import('types/config').ValidatedConfig} */ (mocked),
+		// @ts-expect-error
+		build_data: {},
+		// @ts-expect-error
+		log: {}
+	});
+
+	const dest = join(__dirname, 'output');
+
+	rmSync(dest, { recursive: true, force: true });
+	builder.writeStatic(dest);
+
+	assert.equal(
+		glob('**', {
+			cwd: /** @type {import('types/config').ValidatedConfig} */ (mocked).kit.files.assets
+		}),
+		glob('**', { cwd: dest })
+	);
+
+	rmSync(dest, { recursive: true, force: true });
+	builder.writeClient(dest);
+
+	assert.equal(
+		glob('**', { cwd: `${cwd}/${SVELTE_KIT}/output/client` }),
+		glob('**', { cwd: dest })
+	);
+
+	rmSync(dest, { recursive: true, force: true });
+	builder.writeServer(dest);
+
+	assert.equal(
+		glob('**', { cwd: `${cwd}/${SVELTE_KIT}/output/server` }),
+		glob('**', { cwd: dest })
+	);
+
+	rmSync(dest, { force: true, recursive: true });
+});
+
+test.run();
diff --git a/packages/kit/src/core/adapt/test/fixtures/basic/.svelte-kit/output/client/routes/index.js b/packages/kit/src/core/adapt/fixtures/basic/.svelte-kit/output/client/routes/index.js
similarity index 100%
rename from packages/kit/src/core/adapt/test/fixtures/basic/.svelte-kit/output/client/routes/index.js
rename to packages/kit/src/core/adapt/fixtures/basic/.svelte-kit/output/client/routes/index.js
diff --git a/packages/kit/src/core/adapt/test/fixtures/basic/.svelte-kit/output/server/app.js b/packages/kit/src/core/adapt/fixtures/basic/.svelte-kit/output/server/app.js
similarity index 100%
rename from packages/kit/src/core/adapt/test/fixtures/basic/.svelte-kit/output/server/app.js
rename to packages/kit/src/core/adapt/fixtures/basic/.svelte-kit/output/server/app.js
diff --git a/packages/kit/src/core/adapt/test/fixtures/basic/static/answer.md b/packages/kit/src/core/adapt/fixtures/basic/static/answer.md
similarity index 100%
rename from packages/kit/src/core/adapt/test/fixtures/basic/static/answer.md
rename to packages/kit/src/core/adapt/fixtures/basic/static/answer.md
diff --git a/packages/kit/src/core/adapt/prerender/prerender.js b/packages/kit/src/core/adapt/prerender/prerender.js
index acbf9d434..df725a59d 100644
--- a/packages/kit/src/core/adapt/prerender/prerender.js
+++ b/packages/kit/src/core/adapt/prerender/prerender.js
@@ -111,6 +111,25 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
 
 	const q = queue(config.kit.prerender.concurrency);
 
+	/**
+	 * @param {string} path
+	 * @param {boolean} is_html
+	 */
+	function output_filename(path, is_html) {
+		if (path === '/') {
+			return '/index.html';
+		}
+		const parts = path.split('/');
+		if (is_html && parts[parts.length - 1] !== 'index.html') {
+			if (config.kit.prerender.createIndexFiles) {
+				parts.push('index.html');
+			} else {
+				parts[parts.length - 1] += '.html';
+			}
+		}
+		return parts.join('/');
+	}
+
 	/**
 	 * @param {string} decoded_path
 	 * @param {string?} referrer
@@ -149,12 +168,7 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
 			const type = rendered.headers.get('content-type');
 			const is_html = response_type === REDIRECT || type === 'text/html';
 
-			const parts = decoded_path.split('/');
-			if (is_html && parts[parts.length - 1] !== 'index.html') {
-				parts.push('index.html');
-			}
-
-			const file = `${out}${parts.join('/')}`;
+			const file = `${out}${output_filename(decoded_path, is_html)}`;
 
 			if (response_type === REDIRECT) {
 				const location = rendered.headers.get('location');
@@ -199,12 +213,7 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
 
 				const is_html = headers.get('content-type') === 'text/html';
 
-				const parts = dependency_path.split('/');
-				if (is_html && parts[parts.length - 1] !== 'index.html') {
-					parts.push('index.html');
-				}
-
-				const file = `${out}${parts.join('/')}`;
+				const file = `${out}${output_filename(dependency_path, is_html)}`;
 				mkdirp(dirname(file));
 
 				writeFileSync(
diff --git a/packages/kit/src/core/build/build_client.js b/packages/kit/src/core/build/build_client.js
index b7198f1fd..40bc80832 100644
--- a/packages/kit/src/core/build/build_client.js
+++ b/packages/kit/src/core/build/build_client.js
@@ -60,7 +60,7 @@ export async function build_client({
 	});
 
 	/** @type {[any, string[]]} */
-	const [merged_config, conflicts] = deep_merge(config.kit.vite(), {
+	const [merged_config, conflicts] = deep_merge(await config.kit.vite(), {
 		configFile: false,
 		root: cwd,
 		base: assets_base,
@@ -87,7 +87,7 @@ export async function build_client({
 				extensions: config.extensions,
 				emitCss: !config.kit.amp,
 				compilerOptions: {
-					hydratable: !!config.kit.hydrate
+					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 be3423eb7..01a60d193 100644
--- a/packages/kit/src/core/build/build_server.js
+++ b/packages/kit/src/core/build/build_server.js
@@ -79,7 +79,7 @@ export class App {
 				error.stack = this.options.get_stack(error);
 			},
 			hooks,
-			hydrate: ${s(config.kit.hydrate)},
+			hydrate: ${s(config.kit.browser.hydrate)},
 			manifest,
 			method_override: ${s(config.kit.methodOverride)},
 			paths: { base, assets },
@@ -88,7 +88,7 @@ export class App {
 			read,
 			root,
 			service_worker: ${has_service_worker ? "base + '/service-worker.js'" : 'null'},
-			router: ${s(config.kit.router)},
+			router: ${s(config.kit.browser.router)},
 			target: ${s(config.kit.target)},
 			template,
 			template_contains_nonce: ${template.includes('%svelte.nonce%')},
@@ -181,7 +181,7 @@ export async function build_server(
 	);
 
 	/** @type {import('vite').UserConfig} */
-	const vite_config = config.kit.vite();
+	const vite_config = await config.kit.vite();
 
 	const default_config = {
 		build: {
@@ -217,7 +217,7 @@ export async function build_server(
 			svelte({
 				extensions: config.extensions,
 				compilerOptions: {
-					hydratable: !!config.kit.hydrate
+					hydratable: !!config.kit.browser.hydrate
 				}
 			})
 		],
diff --git a/packages/kit/src/core/build/build_service_worker.js b/packages/kit/src/core/build/build_service_worker.js
index adf4d1a51..e4779a059 100644
--- a/packages/kit/src/core/build/build_service_worker.js
+++ b/packages/kit/src/core/build/build_service_worker.js
@@ -56,7 +56,7 @@ export async function build_service_worker(
 	);
 
 	/** @type {[any, string[]]} */
-	const [merged_config, conflicts] = deep_merge(config.kit.vite(), {
+	const [merged_config, conflicts] = deep_merge(await config.kit.vite(), {
 		configFile: false,
 		root: cwd,
 		base: assets_base,
diff --git a/packages/kit/src/core/build/index.js b/packages/kit/src/core/build/index.js
index 0c6751ae0..df8f43ca1 100644
--- a/packages/kit/src/core/build/index.js
+++ b/packages/kit/src/core/build/index.js
@@ -57,6 +57,7 @@ export async function build(config) {
 	const build_data = {
 		app_dir: config.kit.appDir,
 		manifest_data: options.manifest_data,
+		service_worker: options.service_worker_entry_file ? 'service_worker.js' : null, // TODO make file configurable?
 		client,
 		server,
 		static: options.manifest_data.assets.map((asset) => posixify(asset.file)),
diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js
index 0ef2d5f9f..7dced035f 100644
--- a/packages/kit/src/core/config/index.spec.js
+++ b/packages/kit/src/core/config/index.spec.js
@@ -14,6 +14,10 @@ const get_defaults = (prefix = '') => ({
 		adapter: null,
 		amp: false,
 		appDir: '_app',
+		browser: {
+			hydrate: true,
+			router: true
+		},
 		csp: {
 			mode: 'auto',
 			directives: {
@@ -61,7 +65,7 @@ const get_defaults = (prefix = '') => ({
 		floc: false,
 		headers: undefined,
 		host: undefined,
-		hydrate: true,
+		hydrate: undefined,
 		inlineStyleThreshold: 0,
 		methodOverride: {
 			parameter: '_method',
@@ -81,6 +85,7 @@ const get_defaults = (prefix = '') => ({
 		prerender: {
 			concurrency: 1,
 			crawl: true,
+			createIndexFiles: true,
 			enabled: true,
 			entries: ['*'],
 			force: undefined,
@@ -88,8 +93,8 @@ const get_defaults = (prefix = '') => ({
 			pages: undefined
 		},
 		protocol: undefined,
-		router: true,
-		ssr: null,
+		router: undefined,
+		ssr: undefined,
 		target: null,
 		trailingSlash: 'never'
 	}
diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js
index bda10e275..29df696d0 100644
--- a/packages/kit/src/core/config/options.js
+++ b/packages/kit/src/core/config/options.js
@@ -57,6 +57,11 @@ const options = object(
 				return input;
 			}),
 
+			browser: object({
+				hydrate: boolean(true),
+				router: boolean(true)
+			}),
+
 			csp: object({
 				mode: list(['auto', 'hash', 'nonce']),
 				directives: object({
@@ -106,24 +111,19 @@ const options = object(
 			floc: boolean(false),
 
 			// TODO: remove this for the 1.0 release
-			headers: validate(undefined, (input, keypath) => {
-				if (typeof input !== undefined) {
-					throw new Error(
-						`${keypath} has been removed. See https://github.com/sveltejs/kit/pull/3384 for details`
-					);
-				}
-			}),
+			headers: error(
+				(keypath) =>
+					`${keypath} has been removed. See https://github.com/sveltejs/kit/pull/3384 for details`
+			),
 
 			// TODO: remove this for the 1.0 release
-			host: validate(undefined, (input, keypath) => {
-				if (typeof input !== undefined) {
-					throw new Error(
-						`${keypath} has been removed. See https://github.com/sveltejs/kit/pull/3384 for details`
-					);
-				}
-			}),
+			host: error(
+				(keypath) =>
+					`${keypath} has been removed. See https://github.com/sveltejs/kit/pull/3384 for details`
+			),
 
-			hydrate: boolean(true),
+			// TODO remove for 1.0
+			hydrate: error((keypath) => `${keypath} has been moved to config.kit.browser.hydrate`),
 
 			inlineStyleThreshold: number(0),
 
@@ -186,6 +186,7 @@ const options = object(
 			prerender: object({
 				concurrency: number(1),
 				crawl: boolean(true),
+				createIndexFiles: boolean(true),
 				enabled: boolean(true),
 				entries: validate(['*'], (input, keypath) => {
 					if (!Array.isArray(input) || !input.every((page) => typeof page === 'string')) {
@@ -225,23 +226,19 @@ const options = object(
 				}),
 
 				// TODO: remove this for the 1.0 release
-				pages: validate(undefined, (input, keypath) => {
-					if (typeof input !== undefined) {
-						throw new Error(`${keypath} has been renamed to \`entries\`.`);
-					}
-				})
+				pages: error((keypath) => `${keypath} has been renamed to \`entries\`.`)
 			}),
 
 			// TODO: remove this for the 1.0 release
-			protocol: validate(undefined, (input, keypath) => {
-				if (typeof input !== undefined) {
-					throw new Error(
-						`${keypath} has been removed. See https://github.com/sveltejs/kit/pull/3384 for details`
-					);
-				}
-			}),
+			protocol: error(
+				(keypath) =>
+					`${keypath} has been removed. See https://github.com/sveltejs/kit/pull/3384 for details`
+			),
 
-			router: boolean(true),
+			// TODO remove for 1.0
+			router: error((keypath) => `${keypath} has been moved to config.kit.browser.router`),
+
+			routes: fun((filepath) => !/(?:(?:^_|\/_)|(?:^\.|\/\.)(?!well-known))/.test(filepath)),
 
 			serviceWorker: object({
 				register: boolean(true),
@@ -249,13 +246,10 @@ const options = object(
 			}),
 
 			// TODO remove this for 1.0
-			ssr: validate(null, (input) => {
-				if (input !== undefined) {
-					throw new Error(
-						'config.kit.ssr has been removed — use the handle hook instead: https://kit.svelte.dev/docs#hooks-handle'
-					);
-				}
-			}),
+			ssr: error(
+				(keypath) =>
+					`${keypath} has been removed — use the handle hook instead: https://kit.svelte.dev/docs#hooks-handle'`
+			),
 
 			target: string(null),
 
@@ -434,4 +428,13 @@ function assert_string(input, keypath) {
 	}
 }
 
+/** @param {(keypath?: string) => string} fn */
+function error(fn) {
+	return validate(undefined, (input, keypath) => {
+		if (input !== undefined) {
+			throw new Error(fn(keypath));
+		}
+	});
+}
+
 export default options;
diff --git a/packages/kit/src/core/create_manifest_data/index.js b/packages/kit/src/core/create_manifest_data/index.js
index 12eafda7d..273617842 100644
--- a/packages/kit/src/core/create_manifest_data/index.js
+++ b/packages/kit/src/core/create_manifest_data/index.js
@@ -87,17 +87,16 @@ export default function create_manifest_data({
 				}
 			});
 
-			if (name[0] === '_') {
-				if (name[1] === '_' && !specials.has(name)) {
-					throw new Error(`Files and directories prefixed with __ are reserved (saw ${file})`);
-				}
-
-				return;
+			if (basename.startsWith('__') && !specials.has(name)) {
+				throw new Error(`Files and directories prefixed with __ are reserved (saw ${file})`);
 			}
 
-			if (basename[0] === '.' && basename !== '.well-known') return null;
 			if (!is_dir && !/^(\.[a-z0-9]+)+$/i.test(ext)) return null; // filter out tmp files etc
 
+			if (!config.kit.routes(file)) {
+				return;
+			}
+
 			const segment = is_dir ? basename : name;
 
 			if (/\]\[/.test(segment)) {
diff --git a/packages/kit/src/core/create_manifest_data/index.spec.js b/packages/kit/src/core/create_manifest_data/index.spec.js
index 349237633..dc1a81cc3 100644
--- a/packages/kit/src/core/create_manifest_data/index.spec.js
+++ b/packages/kit/src/core/create_manifest_data/index.spec.js
@@ -3,29 +3,20 @@ import { fileURLToPath } from 'url';
 import { test } from 'uvu';
 import * as assert from 'uvu/assert';
 import create_manifest_data from './index.js';
+import options from '../config/options.js';
 
 const cwd = fileURLToPath(new URL('./test', import.meta.url));
 
 /**
  * @param {string} dir
- * @param {string[]} [extensions]
+ * @param {import('types/config').Config} config
  * @returns
  */
-const create = (dir, extensions = ['.svelte']) => {
-	/** @type {import('types/config').Config} */
-	const initial = {
-		extensions,
-		kit: {
-			files: {
-				assets: path.resolve(cwd, 'static'),
-				routes: path.resolve(cwd, dir)
-			},
-			appDir: '_app',
-			serviceWorker: {
-				files: (filepath) => !/\.DS_STORE/.test(filepath)
-			}
-		}
-	};
+const create = (dir, config = {}) => {
+	const initial = options(config, 'config');
+
+	initial.kit.files.assets = path.resolve(cwd, 'static');
+	initial.kit.files.routes = path.resolve(cwd, dir);
 
 	return create_manifest_data({
 		config: /** @type {import('types/config').ValidatedConfig} */ (initial),
@@ -265,6 +256,83 @@ test('ignores files and directories with leading dots except .well-known', () =>
 	]);
 });
 
+test('ignores files by `kit.excludes` config w/RegExp', () => {
+	const { routes } = create('samples/hidden-by-excludes-config', {
+		kit: {
+			routes: (filepath) => !filepath.endsWith('.test.js') && !filepath.endsWith('.spec.js')
+		}
+	});
+
+	assert.equal(
+		routes
+			.map((r) => r.type === 'endpoint' && r.file)
+			.filter(Boolean)
+			.sort(),
+		[
+			'samples/hidden-by-excludes-config/.a.js',
+			'samples/hidden-by-excludes-config/.well-known/dnt-policy.txt.js',
+			'samples/hidden-by-excludes-config/_a.js',
+			'samples/hidden-by-excludes-config/a.js',
+			'samples/hidden-by-excludes-config/a.md',
+			'samples/hidden-by-excludes-config/subdir/.a.js',
+			'samples/hidden-by-excludes-config/subdir/_a.js',
+			'samples/hidden-by-excludes-config/subdir/.well-known/dnt-policy.txt.js',
+			'samples/hidden-by-excludes-config/subdir/a.js',
+			'samples/hidden-by-excludes-config/subdir/a.md'
+		].sort()
+	);
+});
+
+test('ignores files by `kit.excludes` config w/string', () => {
+	const { routes } = create('samples/hidden-by-excludes-config', {
+		kit: {
+			routes: (filepath) => !filepath.split('/').includes('a.js')
+		}
+	});
+
+	assert.equal(
+		routes
+			.map((r) => r.type === 'endpoint' && r.file)
+			.filter(Boolean)
+			.sort(),
+		[
+			'samples/hidden-by-excludes-config/.a.js',
+			'samples/hidden-by-excludes-config/.well-known/dnt-policy.txt.js',
+			'samples/hidden-by-excludes-config/_a.js',
+			'samples/hidden-by-excludes-config/a.md',
+			'samples/hidden-by-excludes-config/a.spec.js',
+			'samples/hidden-by-excludes-config/subdir/.a.js',
+			'samples/hidden-by-excludes-config/subdir/.well-known/dnt-policy.txt.js',
+			'samples/hidden-by-excludes-config/subdir/_a.js',
+			'samples/hidden-by-excludes-config/subdir/a.md',
+			'samples/hidden-by-excludes-config/subdir/a.spec.js'
+		].sort()
+	);
+});
+
+test('ignores files by `kit.excludes` config w/function', () => {
+	const { routes } = create('samples/hidden-by-excludes-config', {
+		kit: {
+			routes: (filepath) => !filepath.startsWith('samples/hidden-by-excludes-config/subdir')
+		}
+	});
+
+	assert.equal(
+		routes
+			.map((r) => r.type === 'endpoint' && r.file)
+			.filter(Boolean)
+			.sort(),
+		[
+			'samples/hidden-by-excludes-config/.a.js',
+			'samples/hidden-by-excludes-config/.well-known/dnt-policy.txt.js',
+			'samples/hidden-by-excludes-config/_a.js',
+			'samples/hidden-by-excludes-config/a.js',
+			'samples/hidden-by-excludes-config/a.md',
+			'samples/hidden-by-excludes-config/a.spec.js'
+		].sort()
+	);
+});
+
 test('allows multiple slugs', () => {
 	const { routes } = create('samples/multiple-slugs');
 
@@ -303,12 +371,9 @@ test('ignores things that look like lockfiles', () => {
 });
 
 test('works with custom extensions', () => {
-	const { components, routes } = create('samples/custom-extension', [
-		'.jazz',
-		'.beebop',
-		'.funk',
-		'.svelte'
-	]);
+	const { components, routes } = create('samples/custom-extension', {
+		extensions: ['.jazz', '.beebop', '.funk', '.svelte']
+	});
 
 	const index = 'samples/custom-extension/index.funk';
 	const about = 'samples/custom-extension/about.jazz';
diff --git a/packages/kit/src/core/adapt/test/fixtures/prerender/build/nested/index.html b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/.a.js
similarity index 100%
rename from packages/kit/src/core/adapt/test/fixtures/prerender/build/nested/index.html
rename to packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/.a.js
diff --git a/packages/kit/src/core/adapt/test/fixtures/prerender/src/routes/nested/index.svelte b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/.well-known/dnt-policy.txt.js
similarity index 100%
rename from packages/kit/src/core/adapt/test/fixtures/prerender/src/routes/nested/index.svelte
rename to packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/.well-known/dnt-policy.txt.js
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/__error.svelte b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/__error.svelte
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/__layout.svelte b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/__layout.svelte
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/_a.js b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/_a.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/a.js b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/a.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/a.md b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/a.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/a.spec.js b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/a.spec.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.a.js b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.a.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.well-known/dnt-policy.txt.js b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.well-known/dnt-policy.txt.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__error.svelte b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__error.svelte
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__layout.svelte b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__layout.svelte
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/_a.js b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/_a.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.js b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.md b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.spec.js b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.spec.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/core/dev/amp_hook.js b/packages/kit/src/core/dev/amp_hook.js
new file mode 100644
index 000000000..10dca700f
--- /dev/null
+++ b/packages/kit/src/core/dev/amp_hook.js
@@ -0,0 +1,54 @@
+/** @type {import('amphtml-validator').Validator} */
+const amp = await (await import('amphtml-validator')).getInstance();
+
+/** @type {import('types/hooks').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 a1e9a948e..7d340754a 100644
--- a/packages/kit/src/core/dev/index.js
+++ b/packages/kit/src/core/dev/index.js
@@ -41,7 +41,7 @@ export async function dev({ cwd, port, host, https, config }) {
 				strictPort: true
 			}
 		},
-		config.kit.vite()
+		await config.kit.vite()
 	);
 
 	/** @type {[any, string[]]} */
@@ -63,7 +63,7 @@ export async function dev({ cwd, port, host, https, config }) {
 				extensions: config.extensions,
 				emitCss: !config.kit.amp,
 				compilerOptions: {
-					hydratable: !!config.kit.hydrate
+					hydratable: !!config.kit.browser.hydrate
 				}
 			}),
 			await create_plugin(config, cwd)
@@ -85,14 +85,6 @@ export async function dev({ cwd, port, host, https, config }) {
 		merged_config.server.https = https;
 	}
 
-	// by default, when enabling HTTPS in Vite, it also enables HTTP/2
-	// however, node-fetch's Request implementation does not like the HTTP/2 headers
-	// we set a no-op proxy config to force Vite to downgrade to TLS-only
-	// see https://vitejs.dev/config/#server-https
-	if (merged_config.server.https && !merged_config.server.proxy) {
-		merged_config.server.proxy = {};
-	}
-
 	if (port) {
 		merged_config.server.port = port;
 	}
diff --git a/packages/kit/src/core/dev/plugin.js b/packages/kit/src/core/dev/plugin.js
index a8387d836..5e95dfe23 100644
--- a/packages/kit/src/core/dev/plugin.js
+++ b/packages/kit/src/core/dev/plugin.js
@@ -3,7 +3,6 @@ import path from 'path';
 import { URL } from 'url';
 import colors from 'kleur';
 import sirv from 'sirv';
-import { respond } from '../../runtime/server/index.js';
 import { __fetch_polyfill } from '../../install-fetch.js';
 import { create_app } from '../create_app/index.js';
 import create_manifest_data from '../create_manifest_data/index.js';
@@ -12,6 +11,7 @@ import { SVELTE_KIT, SVELTE_KIT_ASSETS } from '../constants.js';
 import { get_mime_lookup, resolve_entry, runtime } from '../utils.js';
 import { coalesce_to_error } from '../../utils/error.js';
 import { load_template } from '../config/index.js';
+import { sequence } from '../../hooks.js';
 
 /**
  * @param {import('types/config').ValidatedConfig} config
@@ -19,14 +19,17 @@ import { load_template } from '../config/index.js';
  * @returns {Promise<import('vite').Plugin>}
  */
 export async function create_plugin(config, cwd) {
-	/** @type {import('amphtml-validator').Validator} */
+	/** @type {import('types/hooks').Handle} */
 	let amp;
 
 	if (config.kit.amp) {
 		process.env.VITE_SVELTEKIT_AMP = 'true';
-		amp = await (await import('amphtml-validator')).getInstance();
+		amp = (await import('./amp_hook.js')).handle;
 	}
 
+	/** @type {import('types/internal').Respond} */
+	const respond = (await import(`${runtime}/server/index.js`)).respond;
+
 	return {
 		name: 'vite-plugin-svelte-kit',
 
@@ -166,10 +169,12 @@ export async function create_plugin(config, cwd) {
 							? await vite.ssrLoadModule(`/${config.kit.files.hooks}`)
 							: {};
 
+						const handle = user_hooks.handle || (({ event, resolve }) => resolve(event));
+
 						/** @type {import('types/internal').Hooks} */
 						const hooks = {
 							getSession: user_hooks.getSession || (() => ({})),
-							handle: user_hooks.handle || (({ event, resolve }) => resolve(event)),
+							handle: amp ? sequence(amp, handle) : handle,
 							handleError:
 								user_hooks.handleError ||
 								(({ /** @type {Error & { frame?: string }} */ error }) => {
@@ -242,7 +247,7 @@ export async function create_plugin(config, cwd) {
 								});
 							},
 							hooks,
-							hydrate: config.kit.hydrate,
+							hydrate: config.kit.browser.hydrate,
 							manifest,
 							method_override: config.kit.methodOverride,
 							paths: {
@@ -253,61 +258,17 @@ export async function create_plugin(config, cwd) {
 							prerender: config.kit.prerender.enabled,
 							read: (file) => fs.readFileSync(path.join(config.kit.files.assets, file)),
 							root,
-							router: config.kit.router,
+							router: config.kit.browser.router,
 							target: config.kit.target,
 							template: ({ head, body, assets, nonce }) => {
-								let rendered = template
-									.replace(/%svelte\.assets%/g, assets)
-									.replace(/%svelte\.nonce%/g, nonce)
-									// head and body must be replaced last, in case someone tries to sneak in %svelte.assets% etc
-									.replace('%svelte.head%', () => head)
-									.replace('%svelte.body%', () => body);
-
-								if (amp) {
-									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 rendered;
+								return (
+									template
+										.replace(/%svelte\.assets%/g, assets)
+										.replace(/%svelte\.nonce%/g, nonce)
+										// head and body must be replaced last, in case someone tries to sneak in %svelte.assets% etc
+										.replace('%svelte.head%', () => head)
+										.replace('%svelte.body%', () => body)
+								);
 							},
 							template_contains_nonce: template.includes('%svelte.nonce%'),
 							trailing_slash: config.kit.trailingSlash
diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js
index a2cb05d6e..c98b8f806 100644
--- a/packages/kit/src/core/generate_manifest/index.js
+++ b/packages/kit/src/core/generate_manifest/index.js
@@ -47,10 +47,15 @@ export function generate_manifest(
 			? (path) => `() => import('${path}')`
 			: (path) => `() => Promise.resolve().then(() => require('${path}'))`;
 
+	const assets = build_data.manifest_data.assets.map((asset) => asset.file);
+	if (build_data.service_worker) {
+		assets.push(build_data.service_worker);
+	}
+
 	// prettier-ignore
 	return `{
 		appDir: ${s(build_data.app_dir)},
-		assets: new Set(${s(build_data.manifest_data.assets.map(asset => asset.file))}),
+		assets: new Set(${s(assets)}),
 		_: {
 			mime: ${s(get_mime_lookup(build_data.manifest_data))},
 			entry: ${s(build_data.client.entry)},
diff --git a/packages/kit/src/core/preview/index.js b/packages/kit/src/core/preview/index.js
index f694665ee..34c3d6a28 100644
--- a/packages/kit/src/core/preview/index.js
+++ b/packages/kit/src/core/preview/index.js
@@ -69,7 +69,7 @@ export async function preview({
 	const app = new App(manifest);
 
 	/** @type {import('vite').UserConfig} */
-	const vite_config = (config.kit.vite && config.kit.vite()) || {};
+	const vite_config = (config.kit.vite && (await config.kit.vite())) || {};
 
 	const server = await get_server(use_https, vite_config, (req, res) => {
 		if (req.url == null) {
diff --git a/packages/kit/src/node.js b/packages/kit/src/node.js
index 44d3ae388..5e70731c7 100644
--- a/packages/kit/src/node.js
+++ b/packages/kit/src/node.js
@@ -52,9 +52,19 @@ function get_raw_body(req) {
 
 /** @type {import('@sveltejs/kit/node').GetRequest} */
 export async function getRequest(base, req) {
+	let headers = /** @type {Record<string, string>} */ (req.headers);
+	if (req.httpVersionMajor === 2) {
+		// we need to strip out the HTTP/2 pseudo-headers because node-fetch's
+		// Request implementation doesn't like them
+		headers = Object.assign({}, headers);
+		delete headers[':method'];
+		delete headers[':path'];
+		delete headers[':authority'];
+		delete headers[':scheme'];
+	}
 	return new Request(base + req.url, {
 		method: req.method,
-		headers: /** @type {Record<string, string>} */ (req.headers),
+		headers,
 		body: await get_raw_body(req) // TODO stream rather than buffer
 	});
 }
diff --git a/packages/kit/src/packaging/index.js b/packages/kit/src/packaging/index.js
index b6584506b..3a288f79c 100644
--- a/packages/kit/src/packaging/index.js
+++ b/packages/kit/src/packaging/index.js
@@ -12,18 +12,23 @@ const essential_files = ['README', 'LICENSE', 'CHANGELOG', '.gitignore', '.npmig
  * @param {string} cwd
  */
 export async function make_package(config, cwd = process.cwd()) {
-	const package_dir = config.kit.package.dir;
-	const abs_package_dir = path.isAbsolute(package_dir) ? package_dir : path.join(cwd, package_dir);
-	rimraf(abs_package_dir);
-	mkdirp(abs_package_dir); // TODO https://github.com/sveltejs/kit/issues/2333
+	if (!fs.existsSync(config.kit.files.lib)) {
+		throw new Error(`${config.kit.files.lib} does not exist`);
+	}
+
+	const package_dir = path.isAbsolute(config.kit.package.dir)
+		? config.kit.package.dir
+		: path.join(cwd, config.kit.package.dir);
+	rimraf(package_dir);
+	mkdirp(package_dir); // TODO https://github.com/sveltejs/kit/issues/2333
 
 	if (config.kit.package.emitTypes) {
 		// Generate type definitions first so hand-written types can overwrite generated ones
 		await emit_dts(config);
 		// Resolve aliases, TS leaves them as-is
-		const files = walk(abs_package_dir);
+		const files = walk(package_dir);
 		for (const file of files) {
-			const filename = path.join(abs_package_dir, file);
+			const filename = path.join(package_dir, file);
 			const source = fs.readFileSync(filename, 'utf8');
 			fs.writeFileSync(filename, resolve_$lib_alias(file, source, config));
 		}
@@ -50,7 +55,7 @@ export async function make_package(config, cwd = process.cwd()) {
 
 		if (!config.kit.package.files(normalized)) {
 			const dts_file = (svelte_ext ? file : file.slice(0, -ext.length)) + '.d.ts';
-			const dts_path = path.join(abs_package_dir, dts_file);
+			const dts_path = path.join(package_dir, dts_file);
 			if (fs.existsSync(dts_path)) {
 				fs.unlinkSync(dts_path);
 
@@ -85,7 +90,7 @@ export async function make_package(config, cwd = process.cwd()) {
 			out_file = file;
 			out_contents = source.toString('utf-8');
 			out_contents = resolve_$lib_alias(out_file, out_contents, config);
-			if (fs.existsSync(path.join(abs_package_dir, out_file))) {
+			if (fs.existsSync(path.join(package_dir, out_file))) {
 				console.warn(
 					'Found already existing file from d.ts generation for ' +
 						out_file +
@@ -101,7 +106,7 @@ export async function make_package(config, cwd = process.cwd()) {
 			out_contents = source;
 		}
 
-		write(path.join(abs_package_dir, out_file), out_contents);
+		write(path.join(package_dir, out_file), out_contents);
 
 		if (config.kit.package.exports(normalized)) {
 			const original = `$lib/${normalized}`;
@@ -148,7 +153,7 @@ export async function make_package(config, cwd = process.cwd()) {
 		}
 	}
 
-	write(path.join(abs_package_dir, 'package.json'), JSON.stringify(pkg, null, '  '));
+	write(path.join(package_dir, 'package.json'), JSON.stringify(pkg, null, 2));
 
 	const whitelist = fs.readdirSync(cwd).filter((file) => {
 		const lowercased = file.toLowerCase();
@@ -158,7 +163,7 @@ export async function make_package(config, cwd = process.cwd()) {
 		const full_path = path.join(cwd, pathname);
 		if (fs.lstatSync(full_path).isDirectory()) continue; // just to be sure
 
-		const package_path = path.join(abs_package_dir, pathname);
+		const package_path = path.join(package_dir, pathname);
 		if (!fs.existsSync(package_path)) fs.copyFileSync(full_path, package_path);
 	}
 
@@ -326,8 +331,7 @@ async function try_load_svelte2tsx() {
 			return await import('svelte2tsx');
 		} catch (e) {
 			throw new Error(
-				'You need to install svelte2tsx and typescript if you want to generate type definitions\n' +
-					e
+				'You need svelte2tsx and typescript if you want to generate type definitions. Install it through your package manager, or disable generation which is highly discouraged. See https://kit.svelte.dev/docs#packaging'
 			);
 		}
 	}
diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js
index 794217e2c..94ba79f20 100644
--- a/packages/kit/src/runtime/server/endpoint.js
+++ b/packages/kit/src/runtime/server/endpoint.js
@@ -116,7 +116,7 @@ function is_pojo(body) {
 		if (body._readableState && body._writableState && body._events) return false;
 
 		// similarly, it could be a web ReadableStream
-		if (body[Symbol.toStringTag] === 'ReadableStream') return false;
+		if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) return false;
 	}
 
 	return true;
diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js
index 66a61a9ff..bb878ae14 100644
--- a/packages/kit/src/runtime/server/page/load_node.js
+++ b/packages/kit/src/runtime/server/page/load_node.js
@@ -105,7 +105,7 @@ export async function load_node({
 				// handle fetch requests for static assets. e.g. prebaked data, etc.
 				// we need to support everything the browser's fetch supports
 				const prefix = options.paths.assets || options.paths.base;
-				const filename = (
+				const filename = decodeURIComponent(
 					resolved.startsWith(prefix) ? resolved.slice(prefix.length) : resolved
 				).slice(1);
 				const filename_html = `${filename}/index.html`; // path may also match path/index.html
diff --git a/packages/kit/types/ambient-modules.d.ts b/packages/kit/types/ambient-modules.d.ts
index 9eefd89b5..083519b0b 100644
--- a/packages/kit/types/ambient-modules.d.ts
+++ b/packages/kit/types/ambient-modules.d.ts
@@ -107,16 +107,10 @@ declare module '$app/stores' {
 	 * Most of the time, you won't need to use it.
 	 */
 	export function getStores<Session = any>(): {
-		navigating: Readable<Navigating | null>;
-		page: Readable<{
-			url: URL;
-			params: Record<string, string>;
-			status: number;
-			error: Error | null;
-		}>;
+		navigating: typeof navigating;
+		page: typeof page;
 		session: Writable<Session>;
 	};
-	export const url: Readable<URL>;
 	/**
 	 * A readable store whose value contains page data.
 	 */
diff --git a/packages/kit/types/config.d.ts b/packages/kit/types/config.d.ts
index 4fadf2ee4..ecba3176e 100644
--- a/packages/kit/types/config.d.ts
+++ b/packages/kit/types/config.d.ts
@@ -118,6 +118,10 @@ export interface Config {
 		adapter?: Adapter;
 		amp?: boolean;
 		appDir?: string;
+		browser?: {
+			hydrate?: boolean;
+			router?: boolean;
+		};
 		csp?: {
 			mode?: 'hash' | 'nonce' | 'auto';
 			directives?: CspDirectives;
@@ -131,7 +135,6 @@ export interface Config {
 			template?: string;
 		};
 		floc?: boolean;
-		hydrate?: boolean;
 		inlineStyleThreshold?: number;
 		methodOverride?: {
 			parameter?: string;
@@ -150,11 +153,12 @@ export interface Config {
 		prerender?: {
 			concurrency?: number;
 			crawl?: boolean;
+			createIndexFiles?: boolean;
 			enabled?: boolean;
 			entries?: string[];
 			onError?: PrerenderOnErrorValue;
 		};
-		router?: boolean;
+		routes?: (filepath: string) => boolean;
 		serviceWorker?: {
 			register?: boolean;
 			files?: (filepath: string) => boolean;
diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts
index 8fa01b89a..294ed943d 100644
--- a/packages/kit/types/internal.d.ts
+++ b/packages/kit/types/internal.d.ts
@@ -209,6 +209,7 @@ export interface ManifestData {
 export interface BuildData {
 	app_dir: string;
 	manifest_data: ManifestData;
+	service_worker: string | null;
 	client: {
 		assets: OutputAsset[];
 		chunks: OutputChunk[];
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 559be82ff..71fa8a064 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -13,7 +13,7 @@ importers:
       '@typescript-eslint/eslint-plugin': ^5.5.0
       '@typescript-eslint/parser': ^5.5.0
       action-deploy-docs: github:sveltejs/action-deploy-docs#main
-      dotenv: ^14.0.0
+      dotenv: ^15.0.0
       eslint: ^8.3.0
       eslint-plugin-import: ^2.25.3
       eslint-plugin-svelte3: ^3.2.1
@@ -31,7 +31,7 @@ importers:
       '@typescript-eslint/eslint-plugin': 5.5.0_15fb0f7dd5018b02e6608eb3a323af2f
       '@typescript-eslint/parser': 5.5.0_eslint@8.3.0+typescript@4.4.4
       action-deploy-docs: github.com/sveltejs/action-deploy-docs/4b34fbdb9caf29ad769ec1dfc955bd1c61755f91
-      dotenv: 14.1.0
+      dotenv: 15.0.0
       eslint: 8.3.0
       eslint-plugin-import: 2.25.3_eslint@8.3.0
       eslint-plugin-svelte3: 3.2.1_eslint@8.3.0
@@ -253,7 +253,7 @@ importers:
       svelte: ^3.44.2
       svelte-check: ^2.2.10
       svelte-preprocess: ^4.9.8
-      svelte2tsx: ~0.4.10
+      svelte2tsx: ~0.5.0
       tiny-glob: ^0.2.9
       uvu: ^0.5.2
       vite: ^2.7.2
@@ -287,7 +287,7 @@ importers:
       svelte: 3.44.2
       svelte-check: 2.2.10_svelte@3.44.2
       svelte-preprocess: 4.9.8_svelte@3.44.2+typescript@4.4.4
-      svelte2tsx: 0.4.10_svelte@3.44.2+typescript@4.4.4
+      svelte2tsx: 0.5.0_svelte@3.44.2+typescript@4.4.4
       tiny-glob: 0.2.9
       uvu: 0.5.2
 
@@ -2341,8 +2341,8 @@ packages:
       esutils: 2.0.3
     dev: true
 
-  /dotenv/14.1.0:
-    resolution: {integrity: sha512-h8V+Yfa8m0YSjf3Rgbno51cxWldb4PEixIJVL55VmW7uAfeLQKiaPrEUiBps+ARK9MeqjJgTf269OMmu6lOODQ==}
+  /dotenv/15.0.0:
+    resolution: {integrity: sha512-/l1sXXm79ry34KwwS0y4oVZjB468iw/6u9g1W26dtexKcIJAnVL2pMF+hxQwzZ7LutxOwEgtym9eIxvX33CMKg==}
     engines: {node: '>=12'}
     dev: true
 
@@ -5040,8 +5040,8 @@ packages:
     engines: {node: '>= 8'}
     dev: true
 
-  /svelte2tsx/0.4.10_svelte@3.44.2+typescript@4.4.4:
-    resolution: {integrity: sha512-vjFzAHKWzyYGz5Xq7Qm1IezOVYf8/jqDNdw+twEj9bfu3RPht9DCLQoMZzIq36IrQZWnb7p2wpDUnzMBASO7vA==}
+  /svelte2tsx/0.5.0_svelte@3.44.2+typescript@4.4.4:
+    resolution: {integrity: sha512-pgMMPzPylwzb/XTLXZQ0y6LUlYzVTep4537NJzHNyTbML864pfVS27iVFOgUK7XmKLyuIdG7tgweUZTsIwgKHw==}
     peerDependencies:
       svelte: ^3.24
       typescript: ^4.1.2


Test patch:
diff --git a/packages/adapter-static/test/apps/prerendered/package.json b/packages/adapter-static/test/apps/prerendered/package.json
index 71b8a3463..364ccc8cd 100644
--- a/packages/adapter-static/test/apps/prerendered/package.json
+++ b/packages/adapter-static/test/apps/prerendered/package.json
@@ -1,6 +1,7 @@
 {
 	"name": "~TODO~",
 	"version": "0.0.1",
+	"private": true,
 	"scripts": {
 		"dev": "../../../../kit/svelte-kit.js dev",
 		"build": "../../../../kit/svelte-kit.js build",
diff --git a/packages/adapter-static/test/apps/spa/package.json b/packages/adapter-static/test/apps/spa/package.json
index aa14bafe5..df8d3986e 100644
--- a/packages/adapter-static/test/apps/spa/package.json
+++ b/packages/adapter-static/test/apps/spa/package.json
@@ -1,6 +1,7 @@
 {
 	"name": "~TODO~",
 	"version": "0.0.1",
+	"private": true,
 	"scripts": {
 		"dev": "../../../../kit/svelte-kit.js dev",
 		"build": "../../../../kit/svelte-kit.js build",
diff --git a/packages/kit/src/core/adapt/test/fixtures/prerender/.svelte-kit/output/server/app.js b/packages/kit/src/core/adapt/test/fixtures/prerender/.svelte-kit/output/server/app.js
deleted file mode 100644
index 554862072..000000000
--- a/packages/kit/src/core/adapt/test/fixtures/prerender/.svelte-kit/output/server/app.js
+++ /dev/null
@@ -1,12 +0,0 @@
-export class App {
-	render() {
-		return new Response('', {
-			status: 200,
-			headers: {
-				'content-type': 'text/html'
-			}
-		});
-	}
-}
-
-export function override() {}
diff --git a/packages/kit/src/core/adapt/test/fixtures/prerender/.svelte-kit/output/server/manifest.js b/packages/kit/src/core/adapt/test/fixtures/prerender/.svelte-kit/output/server/manifest.js
deleted file mode 100644
index 570899a6d..000000000
--- a/packages/kit/src/core/adapt/test/fixtures/prerender/.svelte-kit/output/server/manifest.js
+++ /dev/null
@@ -1,1 +0,0 @@
-export const manifest = {};
diff --git a/packages/kit/src/core/adapt/test/index.js b/packages/kit/src/core/adapt/test/index.js
deleted file mode 100644
index 2c212dee5..000000000
--- a/packages/kit/src/core/adapt/test/index.js
+++ /dev/null
@@ -1,138 +0,0 @@
-import { rmSync } from 'fs';
-import { join } from 'path';
-import * as uvu from 'uvu';
-import * as assert from 'uvu/assert';
-import glob from 'tiny-glob/sync.js';
-import { create_builder } from '../builder.js';
-import { fileURLToPath } from 'url';
-import { SVELTE_KIT } from '../../constants.js';
-
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = join(__filename, '..');
-
-/** @param {string} _msg */
-const logger = (_msg) => {};
-
-/** @type {import('types/internal').Logger} */
-const log = Object.assign(logger, {
-	info: logger,
-	minor: logger,
-	warn: logger,
-	error: logger,
-	success: logger
-});
-
-const suite = uvu.suite('adapter');
-
-suite('copy files', () => {
-	const cwd = join(__dirname, 'fixtures/basic');
-
-	/** @type {import('types/config').Config} */
-	const mocked = {
-		extensions: ['.svelte'],
-		kit: {
-			appDir: '_app',
-			files: {
-				assets: join(__dirname, 'fixtures/basic/static')
-			}
-		}
-	};
-
-	/** @type {import('types/internal').BuildData} */
-	const build_data = {
-		// @ts-expect-error
-		client: {},
-		// @ts-expect-error
-		server: {},
-		static: [],
-		entries: []
-	};
-
-	const builder = create_builder({
-		cwd,
-		config: /** @type {import('types/config').ValidatedConfig} */ (mocked),
-		build_data,
-		log
-	});
-
-	const dest = join(__dirname, 'output');
-
-	rmSync(dest, { recursive: true, force: true });
-	builder.writeStatic(dest);
-
-	assert.equal(
-		glob('**', {
-			cwd: /** @type {import('types/config').ValidatedConfig} */ (mocked).kit.files.assets
-		}),
-		glob('**', { cwd: dest })
-	);
-
-	rmSync(dest, { recursive: true, force: true });
-	builder.writeClient(dest);
-
-	assert.equal(
-		glob('**', { cwd: `${cwd}/${SVELTE_KIT}/output/client` }),
-		glob('**', { cwd: dest })
-	);
-
-	rmSync(dest, { recursive: true, force: true });
-	builder.writeServer(dest);
-
-	assert.equal(
-		glob('**', { cwd: `${cwd}/${SVELTE_KIT}/output/server` }),
-		glob('**', { cwd: dest })
-	);
-});
-
-suite('prerender', async () => {
-	const cwd = join(__dirname, 'fixtures/prerender');
-	const prerendered_files = join(__dirname, 'fixtures/prerender/build');
-
-	/** @type {import('types/config').Config} */
-	const mocked = {
-		extensions: ['.svelte'],
-		kit: {
-			files: {
-				assets: join(__dirname, 'fixtures/prerender/static'),
-				routes: join(__dirname, 'fixtures/prerender/src/routes')
-			},
-			appDir: '_app',
-			prerender: {
-				concurrency: 1,
-				enabled: true,
-				entries: ['*']
-			}
-		}
-	};
-
-	/** @type {import('types/internal').BuildData} */
-	const build_data = {
-		// @ts-expect-error
-		client: { assets: [], chunks: [] },
-		// @ts-expect-error
-		server: { chunks: [] },
-		static: [],
-		entries: ['/nested']
-	};
-
-	const builder = create_builder({
-		cwd,
-		config: /** @type {import('types/config').ValidatedConfig} */ (mocked),
-		build_data,
-		log
-	});
-
-	const dest = join(__dirname, 'output');
-
-	rmSync(dest, { recursive: true, force: true });
-	await builder.prerender({
-		all: true,
-		dest
-	});
-
-	assert.equal(glob('**', { cwd: prerendered_files }), glob('**', { cwd: dest }));
-
-	rmSync(dest, { recursive: true, force: true });
-});
-
-suite.run();
diff --git a/packages/kit/src/packaging/test/errors/duplicate-export/package.json b/packages/kit/src/packaging/test/errors/duplicate-export/package.json
index 696dcdb26..4befdcc6b 100644
--- a/packages/kit/src/packaging/test/errors/duplicate-export/package.json
+++ b/packages/kit/src/packaging/test/errors/duplicate-export/package.json
@@ -1,5 +1,6 @@
 {
-	"name": "errors",
+	"name": "error-duplicate-export",
+	"private": true,
 	"version": "1.0.0",
-	"description": "validate error output"
+	"description": "validate duplicate export error output"
 }
diff --git a/packages/kit/src/packaging/test/errors/no-lib-folder/package.json b/packages/kit/src/packaging/test/errors/no-lib-folder/package.json
new file mode 100644
index 000000000..060a9474e
--- /dev/null
+++ b/packages/kit/src/packaging/test/errors/no-lib-folder/package.json
@@ -0,0 +1,6 @@
+{
+	"name": "error-no-lib-folder",
+	"private": true,
+	"version": "1.0.0",
+	"description": "validate no lib folder error output"
+}
diff --git a/packages/kit/src/packaging/test/errors/no-lib-folder/svelte.config.js b/packages/kit/src/packaging/test/errors/no-lib-folder/svelte.config.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/src/packaging/test/fixtures/assets/expected/package.json b/packages/kit/src/packaging/test/fixtures/assets/expected/package.json
index 4ce122a1a..8cd8408fe 100644
--- a/packages/kit/src/packaging/test/fixtures/assets/expected/package.json
+++ b/packages/kit/src/packaging/test/fixtures/assets/expected/package.json
@@ -1,5 +1,6 @@
 {
   "name": "assets",
+	"private": true,
   "version": "1.0.0",
   "description": "no tampering assets",
   "type": "module",
diff --git a/packages/kit/src/packaging/test/fixtures/assets/package.json b/packages/kit/src/packaging/test/fixtures/assets/package.json
index ee3ef4904..f25793367 100644
--- a/packages/kit/src/packaging/test/fixtures/assets/package.json
+++ b/packages/kit/src/packaging/test/fixtures/assets/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "assets",
+	"private": true,
 	"version": "1.0.0",
 	"description": "no tampering assets"
 }
diff --git a/packages/kit/src/packaging/test/fixtures/emitTypes-false/expected/package.json b/packages/kit/src/packaging/test/fixtures/emitTypes-false/expected/package.json
index b92c803cd..c4ccc94b1 100644
--- a/packages/kit/src/packaging/test/fixtures/emitTypes-false/expected/package.json
+++ b/packages/kit/src/packaging/test/fixtures/emitTypes-false/expected/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "emit-types",
+	"private": true,
 	"version": "1.0.0",
 	"description": "emitTypes settings disabled",
 	"type": "module",
@@ -10,4 +11,4 @@
 		".": "./index.js"
 	},
 	"svelte": "./index.js"
-}
+}
\ No newline at end of file
diff --git a/packages/kit/src/packaging/test/fixtures/emitTypes-false/package.json b/packages/kit/src/packaging/test/fixtures/emitTypes-false/package.json
index bdd9bbbaa..ec97559d2 100644
--- a/packages/kit/src/packaging/test/fixtures/emitTypes-false/package.json
+++ b/packages/kit/src/packaging/test/fixtures/emitTypes-false/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "emit-types",
+	"private": true,
 	"version": "1.0.0",
 	"description": "emitTypes settings disabled",
 	"type": "module"
diff --git a/packages/kit/src/packaging/test/fixtures/exports-merge/expected/package.json b/packages/kit/src/packaging/test/fixtures/exports-merge/expected/package.json
index 36398680c..085f6ac97 100644
--- a/packages/kit/src/packaging/test/fixtures/exports-merge/expected/package.json
+++ b/packages/kit/src/packaging/test/fixtures/exports-merge/expected/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "exports-replace",
+	"private": true,
 	"version": "1.0.0",
 	"description": "merges exports field",
 	"exports": {
@@ -18,4 +19,4 @@
 	},
 	"type": "module",
 	"svelte": "./index.js"
-}
+}
\ No newline at end of file
diff --git a/packages/kit/src/packaging/test/fixtures/exports-merge/package.json b/packages/kit/src/packaging/test/fixtures/exports-merge/package.json
index 0607e2902..0033602dd 100644
--- a/packages/kit/src/packaging/test/fixtures/exports-merge/package.json
+++ b/packages/kit/src/packaging/test/fixtures/exports-merge/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "exports-replace",
+	"private": true,
 	"version": "1.0.0",
 	"description": "merges exports field",
 	"exports": {
diff --git a/packages/kit/src/packaging/test/fixtures/exports-replace/expected/package.json b/packages/kit/src/packaging/test/fixtures/exports-replace/expected/package.json
index 64f5503df..45ec663ff 100644
--- a/packages/kit/src/packaging/test/fixtures/exports-replace/expected/package.json
+++ b/packages/kit/src/packaging/test/fixtures/exports-replace/expected/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "exports-replace",
+	"private": true,
 	"version": "1.0.0",
 	"description": "replaces exports field",
 	"type": "module",
@@ -10,4 +11,4 @@
 		"./package.json": "./package.json"
 	},
 	"svelte": "./Test.svelte"
-}
+}
\ No newline at end of file
diff --git a/packages/kit/src/packaging/test/fixtures/exports-replace/package.json b/packages/kit/src/packaging/test/fixtures/exports-replace/package.json
index 9a1f196ad..96c198738 100644
--- a/packages/kit/src/packaging/test/fixtures/exports-replace/package.json
+++ b/packages/kit/src/packaging/test/fixtures/exports-replace/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "exports-replace",
+	"private": true,
 	"version": "1.0.0",
 	"description": "replaces exports field",
 	"type": "module",
diff --git a/packages/kit/src/packaging/test/fixtures/files-exclude/expected/package.json b/packages/kit/src/packaging/test/fixtures/files-exclude/expected/package.json
index 43785b17d..2303af531 100644
--- a/packages/kit/src/packaging/test/fixtures/files-exclude/expected/package.json
+++ b/packages/kit/src/packaging/test/fixtures/files-exclude/expected/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "files-exclude",
+	"private": true,
 	"version": "1.0.0",
 	"description": "files.exclude settings configured",
 	"type": "module",
@@ -10,4 +11,4 @@
 		".": "./index.js"
 	},
 	"svelte": "./index.js"
-}
+}
\ No newline at end of file
diff --git a/packages/kit/src/packaging/test/fixtures/files-exclude/package.json b/packages/kit/src/packaging/test/fixtures/files-exclude/package.json
index aa6ad65f0..c81333c30 100644
--- a/packages/kit/src/packaging/test/fixtures/files-exclude/package.json
+++ b/packages/kit/src/packaging/test/fixtures/files-exclude/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "files-exclude",
+	"private": true,
 	"version": "1.0.0",
 	"description": "files.exclude settings configured",
 	"type": "module"
diff --git a/packages/kit/src/packaging/test/fixtures/javascript/expected/package.json b/packages/kit/src/packaging/test/fixtures/javascript/expected/package.json
index 61989d1d8..bbabe850c 100644
--- a/packages/kit/src/packaging/test/fixtures/javascript/expected/package.json
+++ b/packages/kit/src/packaging/test/fixtures/javascript/expected/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "javascript",
+	"private": true,
 	"version": "1.0.0",
 	"description": "standard javascript package",
 	"type": "module",
@@ -13,4 +14,4 @@
 		".": "./index.js"
 	},
 	"svelte": "./index.js"
-}
+}
\ No newline at end of file
diff --git a/packages/kit/src/packaging/test/fixtures/javascript/package.json b/packages/kit/src/packaging/test/fixtures/javascript/package.json
index c568fbfa5..77440f772 100644
--- a/packages/kit/src/packaging/test/fixtures/javascript/package.json
+++ b/packages/kit/src/packaging/test/fixtures/javascript/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "javascript",
+	"private": true,
 	"version": "1.0.0",
 	"description": "standard javascript package"
 }
diff --git a/packages/kit/src/packaging/test/fixtures/resolve-alias/expected/package.json b/packages/kit/src/packaging/test/fixtures/resolve-alias/expected/package.json
index 78ebd4d10..bb3d7d208 100644
--- a/packages/kit/src/packaging/test/fixtures/resolve-alias/expected/package.json
+++ b/packages/kit/src/packaging/test/fixtures/resolve-alias/expected/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "resolve-alias",
+	"private": true,
 	"version": "1.0.0",
 	"description": "package using $lib alias",
 	"type": "module",
@@ -12,4 +13,4 @@
 		"./sub/foo": "./sub/foo.js"
 	},
 	"svelte": "./index.js"
-}
+}
\ No newline at end of file
diff --git a/packages/kit/src/packaging/test/fixtures/resolve-alias/package.json b/packages/kit/src/packaging/test/fixtures/resolve-alias/package.json
index eec695b67..19ecfdbec 100644
--- a/packages/kit/src/packaging/test/fixtures/resolve-alias/package.json
+++ b/packages/kit/src/packaging/test/fixtures/resolve-alias/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "resolve-alias",
+	"private": true,
 	"version": "1.0.0",
 	"description": "package using $lib alias",
 	"type": "module"
diff --git a/packages/kit/src/packaging/test/fixtures/typescript/expected/package.json b/packages/kit/src/packaging/test/fixtures/typescript/expected/package.json
index 16dc1669f..91b6a3b5b 100644
--- a/packages/kit/src/packaging/test/fixtures/typescript/expected/package.json
+++ b/packages/kit/src/packaging/test/fixtures/typescript/expected/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "typescript",
+	"private": true,
 	"version": "1.0.0",
 	"description": "standard typescript package",
 	"type": "module",
@@ -12,4 +13,4 @@
 		".": "./index.js"
 	},
 	"svelte": "./index.js"
-}
+}
\ No newline at end of file
diff --git a/packages/kit/src/packaging/test/fixtures/typescript/package.json b/packages/kit/src/packaging/test/fixtures/typescript/package.json
index a2feaf4d4..7e166c721 100644
--- a/packages/kit/src/packaging/test/fixtures/typescript/package.json
+++ b/packages/kit/src/packaging/test/fixtures/typescript/package.json
@@ -1,5 +1,6 @@
 {
 	"name": "typescript",
+	"private": true,
 	"version": "1.0.0",
 	"description": "standard typescript package",
 	"type": "module"
diff --git a/packages/kit/src/packaging/test/index.js b/packages/kit/src/packaging/test/index.js
index e51b9ac05..cdad2d3ee 100644
--- a/packages/kit/src/packaging/test/index.js
+++ b/packages/kit/src/packaging/test/index.js
@@ -1,4 +1,4 @@
-import { statSync, readFileSync } from 'fs';
+import fs from 'fs';
 import { join } from 'path';
 import { fileURLToPath } from 'url';
 
@@ -37,11 +37,11 @@ async function test_make_package(path) {
 		const extensions = ['.json', '.svelte', '.ts', 'js'];
 		for (const file of actual_files) {
 			const pathname = join(pwd, file);
-			if (statSync(pathname).isDirectory()) continue;
+			if (fs.statSync(pathname).isDirectory()) continue;
 			assert.ok(expected_files.includes(file), `Did not expect ${file} in ${path}`);
 
-			const expected = readFileSync(join(ewd, file));
-			const actual = readFileSync(join(pwd, file));
+			const expected = fs.readFileSync(join(ewd, file));
+			const actual = fs.readFileSync(join(pwd, file));
 			const err_msg = `Expected equal file contents for ${file} in ${path}`;
 
 			if (extensions.some((ext) => pathname.endsWith(ext))) {
@@ -75,25 +75,43 @@ function format(file, content) {
 	});
 }
 
-test('standard package errors', async () => {
-	// TODO: refactor and allow this to handle multiple errors
-	const cwd = join(__dirname, 'errors', 'duplicate-export');
-	const pwd = join(cwd, 'package');
+for (const dir of fs.readdirSync(join(__dirname, 'errors'))) {
+	test(`package error [${dir}]`, async () => {
+		const cwd = join(__dirname, 'errors', dir);
+		const pwd = join(cwd, 'package');
 
-	const config = await load_config({ cwd });
-	try {
-		// TODO: use assert.throws, does not seem to work with async/await
-		await make_package(config, cwd);
-	} catch (/** @type {any} */ e) {
-		assert.equal(
-			e.message,
-			'Duplicate "./utils" export. Please remove or rename either $lib/utils/index.js or $lib/utils.ts',
-			'Duplicate export are not thrown'
-		);
-	} finally {
-		rimraf(pwd);
-	}
-});
+		const config = await load_config({ cwd });
+		try {
+			await make_package(config, cwd);
+			assert.unreachable('Must not pass make_package');
+		} catch (/** @type {any} */ error) {
+			assert.instance(error, Error);
+			switch (dir) {
+				case 'duplicate-export':
+					assert.match(
+						error.message,
+						'Duplicate "./utils" export. Please remove or rename either $lib/utils/index.js or $lib/utils.ts'
+					);
+					break;
+				case 'no-lib-folder':
+					assert.match(error.message, `${join(cwd, 'src', 'lib')} does not exist`);
+					break;
+				// TODO: non-existent tsconfig passes without error
+				// 	it detects tsconfig in packages/kit instead and creates package folder
+				// 	in packages/kit/package, not sure how to handle and test this yet
+				// case 'no-tsconfig':
+				// 	assert.match(error.message, 'Failed to locate tsconfig or jsconfig');
+				// 	break;
+
+				default:
+					assert.unreachable('All error test must be handled');
+					break;
+			}
+		} finally {
+			rimraf(pwd);
+		}
+	});
+}
 
 test('create standard package with javascript', async () => {
 	// should also preserve filename casing
diff --git a/packages/kit/test/prerendering/basics/src/routes/load-file-with-spaces.svelte b/packages/kit/test/prerendering/basics/src/routes/load-file-with-spaces.svelte
new file mode 100644
index 000000000..b0a3d165f
--- /dev/null
+++ b/packages/kit/test/prerendering/basics/src/routes/load-file-with-spaces.svelte
@@ -0,0 +1,25 @@
+<script context="module">
+	/** @type {import('@sveltejs/kit').Load} */
+	export async function load({ fetch }) {
+		const r1 = await fetch('/file%20with%20spaces.json');
+		const p1 = await r1.json();
+
+		const r2 = await fetch('/file with spaces.json');
+		const p2 = await r2.json();
+
+		if (p1.answer !== p2.answer) {
+			throw new Error('oops');
+		}
+
+		return {
+			props: p1
+		};
+	}
+</script>
+
+<script>
+	/** @type {number} */
+	export let answer;
+</script>
+
+<h1>answer: {answer}</h1>
diff --git a/packages/kit/test/prerendering/basics/static/file with spaces.json b/packages/kit/test/prerendering/basics/static/file with spaces.json
new file mode 100644
index 000000000..6e887fd6a
--- /dev/null
+++ b/packages/kit/test/prerendering/basics/static/file with spaces.json
@@ -0,0 +1,1 @@
+{ "answer": 42 }
diff --git a/packages/kit/test/prerendering/basics/test/test.js b/packages/kit/test/prerendering/basics/test/test.js
index 16972c334..2cef10ee3 100644
--- a/packages/kit/test/prerendering/basics/test/test.js
+++ b/packages/kit/test/prerendering/basics/test/test.js
@@ -58,4 +58,9 @@ test('renders page with unbuffered data from endpoint', () => {
 	assert.equal(json, JSON.stringify({ answer: 42 }));
 });
 
+test('loads a file with spaces in the filename', () => {
+	const content = read('load-file-with-spaces/index.html');
+	assert.ok(content.includes('<h1>answer: 42</h1>'), content);
+});
+
 test.run();
diff --git a/packages/kit/test/prerendering/options/svelte.config.js b/packages/kit/test/prerendering/options/svelte.config.js
index ac5499a0d..28e2e0b4f 100644
--- a/packages/kit/test/prerendering/options/svelte.config.js
+++ b/packages/kit/test/prerendering/options/svelte.config.js
@@ -17,6 +17,10 @@ const config = {
 			assets: 'https://cdn.example.com/stuff'
 		},
 
+		prerender: {
+			createIndexFiles: false
+		},
+
 		vite: {
 			build: {
 				minify: false
diff --git a/packages/kit/test/prerendering/options/test/test.js b/packages/kit/test/prerendering/options/test/test.js
index 46bf27679..edf18c2aa 100644
--- a/packages/kit/test/prerendering/options/test/test.js
+++ b/packages/kit/test/prerendering/options/test/test.js
@@ -15,7 +15,7 @@ test('prerenders /path-base', () => {
 });
 
 test('prerenders nested /path-base', () => {
-	const content = read('/nested/index.html');
+	const content = read('/nested.html');
 	assert.ok(content.includes('<h1>nested hello</h1>'));
 	assert.ok(content.includes('http://sveltekit-prerender/path-base/nested'));
 });