Repository: sveltejs/kit (js)

Issue:
adapter-cloudflare does not bundle service-worker.js since around next.207 - results in 404 and thus failed SW registration
### Describe the bug

Since around next.207 `adapter-cloudflare` seems not to bundle `service-worker.js`

When testing locally, everything seems OK. The SW file is present in the `./svelte-kit/cloudflare` builld folder - as it was with older versions of Sveltekit. When running `npm run preview` the SW is being successfully registered according to Chrome Dev Tools.

After deployment﻿ to CF, and running the site, the following error shows in the console log:
`Uncaught (in promise) TypeError: Failed to register a ServiceWorker for scope ('https://sw-cloudflare-repro.pages.dev/') with script ('https://sw-cloudflare-repro.pages.dev/service-worker.js'): A bad HTTP response code (404) was received when fetching the script.`

Also, according to Dev Tools, the SW is not present.


### Reproduction

[https://github.com/codepainting/sw-cloudflare-repro](https://github.com/codepainting/sw-cloudflare-repro)

Basically it's the standard demo app, with the following additions:
+ `service-worker.js` is included in `/src`
+ `import adapter from '@sveltejs/adapter-cloudflare';` is configured

Demo site:
[https://sw-cloudflare-repro.pages.dev/](https://sw-cloudflare-repro.pages.dev/)

Run demo site.
Look into the console logs.

### Logs

```shell
Uncaught (in promise) TypeError: Failed to register a ServiceWorker for scope ('https://sw-cloudflare-repro.pages.dev/') with script ('https://sw-cloudflare-repro.pages.dev/service-worker.js'): A bad HTTP response code (404) was received when fetching the script.
```


### System Info

```shell
System:
    OS: Linux 4.19 Ubuntu 20.04.3 LTS (Focal Fossa)
    CPU: (4) x64 Intel(R) Core(TM) i5-7600K CPU @ 3.80GHz
    Memory: 19.42 GB / 25.03 GB
    Container: Yes
    Shell: 5.8 - /usr/bin/zsh
  Binaries:
    Node: 16.13.0 - ~/.volta/tools/image/node/16.13.0/bin/node
    Yarn: 1.22.4 - ~/.volta/bin/yarn
    npm: 8.1.4 - ~/.volta/tools/image/npm/8.1.4/bin/npm
  npmPackages:
    @sveltejs/adapter-auto: next => 1.0.0-next.7 
    @sveltejs/adapter-cloudflare: ^1.0.0-next.5 => 1.0.0-next.5 
    @sveltejs/kit: next => 1.0.0-next.213 
    svelte: ^3.44.0 => 3.44.3
```


### Severity

blocking an upgrade

### Additional Information

_No response_

Pull request description:
include service-worker.js in manifest
fixes #3187. `service-worker.js` is an anomaly because it's a generated file, but it lives among the contents of `static`, so it was overlooked previously. No test because the effects are only really observable in certain production environments (Cloudflare specifically, because the handler itself is responsible for serving assets), depending on how they work

### Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
- [x] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
- [x] This message body should clearly illustrate what problems it solves.
- [ ] Ideally, include a test that fails without this PR but passes with it.

### Tests
- [ ] Run the tests with `pnpm test` and lint the project with `pnpm lint` and `pnpm check`

### Changesets
- [x] If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running `pnpx changeset` and following the prompts. All changesets should be `patch` until SvelteKit 1.0


Patch:
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/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/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/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/pre.json b/.changeset/pre.json
index a775af371..b21683ac9 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -10,22 +10,10 @@
     "@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",
-    "prerendering-test-basics": "0.0.1",
-    "prerendering-test-options": "0.0.1"
+    "@sveltejs/adapter-auto": "1.0.0-next"
   },
   "changesets": [
     "afraid-eels-tease",
@@ -134,6 +122,7 @@
     "eight-glasses-sparkle",
     "eight-hats-worry",
     "eight-keys-give",
+    "eight-readers-thank",
     "eighty-candles-grow",
     "eighty-carrots-heal",
     "eighty-waves-obey",
@@ -362,6 +351,7 @@
     "nine-actors-whisper",
     "nine-beans-rhyme",
     "nine-dots-occur",
+    "nine-kids-brake",
     "nine-rats-heal",
     "nine-starfishes-destroy",
     "nine-tomatoes-poke",
@@ -418,6 +408,7 @@
     "pretty-mayflies-tickle",
     "pretty-snakes-burn",
     "proud-bananas-love",
+    "proud-buses-hug",
     "proud-hairs-chew",
     "proud-needles-shake",
     "proud-numbers-promise",
@@ -468,6 +459,7 @@
     "serious-books-push",
     "serious-dolphins-approve",
     "serious-fireants-drum",
+    "serious-horses-rule",
     "serious-vans-arrive",
     "seven-avocados-hug",
     "seven-bags-sniff",
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/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/.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
index dcca025e1..947221885 100644
--- a/.github/workflows/update_template.yml
+++ b/.github/workflows/update_template.yml
@@ -1,11 +1,9 @@
 name: Update Template
 
 on:
-  workflow_run:
-    workflows: [Release]
-    types:
-      - completed
-  workflow_dispatch:
+  push:
+    tags:
+      - create-svelte@*
 
 jobs:
   release:
@@ -51,9 +49,8 @@ jobs:
       - 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 commit -m "create-svelte version ${{ github.ref_name }}"
           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/14-configuration.md b/documentation/docs/14-configuration.md
index 95e3b2f42..9b144f2c8 100644
--- a/documentation/docs/14-configuration.md
+++ b/documentation/docs/14-configuration.md
@@ -58,6 +58,7 @@ const config = {
 			onError: 'fail'
 		},
 		router: true,
+		routes: (filepath) => !/(?:(?:^_|\/_)|(?:^\.|\/\.)(?!well-known))/.test(filepath),
 		serviceWorker: {
 			register: true,
 			files: (filepath) => !/\.DS_STORE/.test(filepath)
@@ -224,6 +225,10 @@ See [Prerendering](#page-options-prerender). An object containing zero or more o
 
 Enables or disables the client-side [router](#page-options-router) app-wide.
 
+### routes
+
+A `(filepath: string) => boolean` function that determines which files create routes and which are treated as [private modules](#routing-private-modules).
+
 ### serviceWorker
 
 An object containing zero or more of the following values:
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..03fc2fa30 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",
diff --git a/packages/create-svelte/CHANGELOG.md b/packages/create-svelte/CHANGELOG.md
index 15f26d6f5..8f75a731f 100644
--- a/packages/create-svelte/CHANGELOG.md
+++ b/packages/create-svelte/CHANGELOG.md
@@ -1,5 +1,11 @@
 # create-svelte
 
+## 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..7dc2a2006 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.102",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/create-svelte/shared/README.md b/packages/create-svelte/shared/README.md
index 82510ca06..2b4623576 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
 
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index 0e0256107..07eceecde 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,19 @@
 # @sveltejs/kit
 
+## 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
diff --git a/packages/kit/package.json b/packages/kit/package.json
index 4bdbad79d..7329bd881 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.0.0-next.246",
+	"version": "1.0.0-next.248",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
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/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/options.js b/packages/kit/src/core/config/options.js
index bda10e275..06b8c426c 100644
--- a/packages/kit/src/core/config/options.js
+++ b/packages/kit/src/core/config/options.js
@@ -243,6 +243,8 @@ const options = object(
 
 			router: boolean(true),
 
+			routes: fun((filepath) => !/(?:(?:^_|\/_)|(?:^\.|\/\.)(?!well-known))/.test(filepath)),
+
 			serviceWorker: object({
 				register: boolean(true),
 				files: fun((filename) => !/\.DS_STORE/.test(filename))
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/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/.well-known/dnt-policy.txt.js b/packages/kit/src/core/create_manifest_data/test/samples/hidden-by-excludes-config/.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/__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/index.js b/packages/kit/src/core/dev/index.js
index a1e9a948e..3bcd1a9b1 100644
--- a/packages/kit/src/core/dev/index.js
+++ b/packages/kit/src/core/dev/index.js
@@ -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 763475d54..97af10bda 100644
--- a/packages/kit/src/core/dev/plugin.js
+++ b/packages/kit/src/core/dev/plugin.js
@@ -25,7 +25,7 @@ export async function create_plugin(config, cwd) {
 
 	if (config.kit.amp) {
 		process.env.VITE_SVELTEKIT_AMP = 'true';
-		amp = (await import('./amp_hook')).handle;
+		amp = (await import('./amp_hook.js')).handle;
 	}
 
 	return {
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/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/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/config.d.ts b/packages/kit/types/config.d.ts
index 4fadf2ee4..19b86cd05 100644
--- a/packages/kit/types/config.d.ts
+++ b/packages/kit/types/config.d.ts
@@ -155,6 +155,7 @@ export interface Config {
 			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[];


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/packaging/test/fixtures/emitTypes-false/expected/package.json b/packages/kit/src/packaging/test/fixtures/emitTypes-false/expected/package.json
index 3c97b0b19..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
@@ -11,4 +11,4 @@
 		".": "./index.js"
 	},
 	"svelte": "./index.js"
-}
+}
\ No newline at end of file
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 87ee8e1ea..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
@@ -19,4 +19,4 @@
 	},
 	"type": "module",
 	"svelte": "./index.js"
-}
+}
\ No newline at end of file
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 e84633d71..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
@@ -11,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/files-exclude/expected/package.json b/packages/kit/src/packaging/test/fixtures/files-exclude/expected/package.json
index 26f20bb77..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
@@ -11,4 +11,4 @@
 		".": "./index.js"
 	},
 	"svelte": "./index.js"
-}
+}
\ No newline at end of file
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 3d7d65e37..bbabe850c 100644
--- a/packages/kit/src/packaging/test/fixtures/javascript/expected/package.json
+++ b/packages/kit/src/packaging/test/fixtures/javascript/expected/package.json
@@ -14,4 +14,4 @@
 		".": "./index.js"
 	},
 	"svelte": "./index.js"
-}
+}
\ No newline at end of file
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 3e59313a2..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
@@ -13,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/typescript/expected/package.json b/packages/kit/src/packaging/test/fixtures/typescript/expected/package.json
index 59ea2b06d..91b6a3b5b 100644
--- a/packages/kit/src/packaging/test/fixtures/typescript/expected/package.json
+++ b/packages/kit/src/packaging/test/fixtures/typescript/expected/package.json
@@ -13,4 +13,4 @@
 		".": "./index.js"
 	},
 	"svelte": "./index.js"
-}
+}
\ No newline at end of file
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();