Repository: sveltejs/kit (js)

Issue:
Tailwind CSS is loaded twice in dev mode
### Describe the bug

It's actually the same bug as reported here: https://github.com/sveltejs/kit/issues/2192

I'm just gonna re-open a new once since I'm getting the same issue on recent ` SvelteKit v1.0.0-next.239`.

I've tried several css loading techniques, they all result in the exact same issue.

When running `svelte-kit dev` and taking a look at the source code, you have tailwindcss twice in there.

it's not happening on the client side, since I guess deduplication or something is happening, but its still confusing and annoying.

Take a look at this repo to reproduce: https://github.com/mrtnbroder/sveltekit-tailwindcss-twice-bug

in `__layout.svelte`, where `global.css` is simply the following:

```
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
```

I've tested these `__layout.svelte`'s

```html
<script lang="ts">
  import "../styles/global.css"
</script>
```
OR
```
<style global lang="postcss">
  @import "../styles/global.css"
<style>
```
OR
```
<style global lang="postcss">
  @import "tailwindcss/base";
  @import "tailwindcss/components";
  @import "tailwindcss/utilities";
<style>
```
OR
```
<style global lang="postcss">
  @tailwind base;
  @tailwind components;
  @tailwind utilities";
<style>
```

### Reproduction

https://github.com/mrtnbroder/sveltekit-tailwindcss-twice-bug

### Logs

_No response_

### System Info

```shell
System:
    OS: macOS 12.1
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
    Memory: 2.13 GB / 16.00 GB
    Shell: 3.3.1 - /usr/local/bin/fish
  Binaries:
    Node: 16.10.0 - ~/.nodenv/versions/16.10.0/bin/node
    Yarn: 1.22.17 - /usr/local/bin/yarn
    npm: 7.24.0 - ~/.nodenv/versions/16.10.0/bin/npm
    Watchman: 2022.01.03.00 - /usr/local/bin/watchman
  Browsers:
    Brave Browser: 97.1.34.80
    Chrome: 97.0.4692.99
    Firefox: 95.0.2
    Safari: 15.2
  npmPackages:
    @sveltejs/kit: ^1.0.0-next.239 => 1.0.0-next.239
    svelte: ^3.46.2 => 3.46.2
```


### Severity

annoyance

### Additional Information

_No response_

Pull request:
[fix] deduplicate injected css during dev
Fixes #3497

During dev, kit inlines css files in order to prevent FOUC. The current implementation injects all imported modules indiscriminately, but this conflicts with vite's own `@import` inlining.

Given a component:
```svelte
<script>
	import '$lib/entry.css';
</script>
```

And the respective css files:
```css
/* entry.css */
@import './base.css';
html { color: green; }
/* entry.css eof */

/* base.css */
html { color: red; }
/* base.css eof */
```

By walking the `ModuleGraph` we end up inlining both `entry.css`'s and `base.css`'s _transformed_ content. Since vite inlines `base` into `entry`, we get the following injected css:
```css
/* entry.css */
/* base.css */
html { color: red; }
/* base.css eof */
html { color: green; }
/* entry.css eof */

/* base.css */
html { color: red; }
/* base.css eof */
```

The first block is `entry` and the second is `base`. If hydration is turned off, you'll get the unexpected rules applied and a confusing inspector experience. If it's turned on, you'll get a flash of _incorrectly styled_ content.

This PR walks through `ssrTransformResult` dependencies instead, such that `base` is ignored since it's already inlined into `entry`. I'm not sure how to test for this, unfortunately.

### Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
- [ ] 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
- [x] 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 `pnpm changeset` and following the prompts. All changesets should be `patch` until SvelteKit 1.0


Patch:
diff --git a/.changeset/beige-mails-accept.md b/.changeset/beige-mails-accept.md
new file mode 100644
index 000000000..4e7857b79
--- /dev/null
+++ b/.changeset/beige-mails-accept.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+Add README
diff --git a/.changeset/five-cows-happen.md b/.changeset/five-cows-happen.md
new file mode 100644
index 000000000..831aded31
--- /dev/null
+++ b/.changeset/five-cows-happen.md
@@ -0,0 +1,5 @@
+---
+"@sveltejs/kit": patch
+---
+
+fix `BodyValidator` for nested interfaces
diff --git a/.changeset/moody-roses-search.md b/.changeset/moody-roses-search.md
new file mode 100644
index 000000000..bd9a43d06
--- /dev/null
+++ b/.changeset/moody-roses-search.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Revert dev command default options
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 5c4f982e8..adf0ef004 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -24,7 +24,8 @@
     "test-basics": "0.0.1",
     "test-options": "0.0.1",
     "test-options-2": "0.0.1",
-    "prerendering-test-trailing-slash": "0.0.1"
+    "prerendering-test-trailing-slash": "0.0.1",
+    "prerendering-test-paths-base": "0.0.1"
   },
   "changesets": [
     "afraid-eels-tease",
@@ -36,6 +37,7 @@
     "beige-beers-explain",
     "beige-eyes-poke",
     "beige-grapes-love",
+    "beige-mails-accept",
     "beige-months-obey",
     "beige-teachers-end",
     "beige-trees-confess",
@@ -213,6 +215,7 @@
     "fifty-taxis-tease",
     "fifty-turtles-joke",
     "five-bags-prove",
+    "five-cows-happen",
     "five-eagles-help",
     "five-oranges-melt",
     "five-poems-pretend",
@@ -324,6 +327,7 @@
     "honest-cows-notice",
     "honest-islands-flash",
     "honest-jars-report",
+    "honest-rats-double",
     "honest-rice-cry",
     "honest-singers-guess",
     "honest-wasps-cough",
@@ -384,6 +388,7 @@
     "lemon-keys-run",
     "lemon-lies-attack",
     "lemon-mails-hope",
+    "lemon-oranges-pump",
     "lemon-taxis-juggle",
     "lemon-ways-doubt",
     "light-kangaroos-tie",
@@ -458,6 +463,7 @@
     "moody-laws-draw",
     "moody-queens-sell",
     "moody-rabbits-complain",
+    "moody-roses-search",
     "moody-scissors-love",
     "moody-sheep-wash",
     "moody-students-report",
@@ -605,6 +611,7 @@
     "rich-ligers-smash",
     "rich-pumpkins-applaud",
     "rich-seahorses-walk",
+    "rich-singers-taste",
     "rotten-cats-pump",
     "rotten-gorillas-sort",
     "rotten-rocks-mate",
@@ -776,6 +783,7 @@
     "stupid-schools-fail",
     "sweet-parents-sell",
     "swift-dingos-glow",
+    "swift-horses-march",
     "swift-jokes-compete",
     "swift-peaches-enjoy",
     "swift-pumpkins-pump",
@@ -928,8 +936,10 @@
     "wild-snails-wait",
     "wise-bees-juggle",
     "wise-bugs-run",
+    "wise-houses-work",
     "wise-pens-ring",
     "wise-rules-cry",
+    "wise-wombats-speak",
     "witty-bottles-press",
     "witty-chicken-teach",
     "witty-eyes-relax",
@@ -939,6 +949,7 @@
     "witty-news-dress",
     "yellow-coins-wash",
     "yellow-eagles-carry",
+    "yellow-eagles-leave",
     "yellow-geese-tap",
     "yellow-meals-agree",
     "yellow-steaks-doubt",
diff --git a/.changeset/rich-singers-taste.md b/.changeset/rich-singers-taste.md
new file mode 100644
index 000000000..4fc754089
--- /dev/null
+++ b/.changeset/rich-singers-taste.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/adapter-vercel': patch
+---
+
+Use general purpose Edge Functions instead of piggybacking Middleware for Edge Deployment + fix split mode
diff --git a/.changeset/swift-horses-march.md b/.changeset/swift-horses-march.md
new file mode 100644
index 000000000..8a28dfcbf
--- /dev/null
+++ b/.changeset/swift-horses-march.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[breaking] delay automatic service worker registration until load event
diff --git a/.changeset/wise-houses-work.md b/.changeset/wise-houses-work.md
new file mode 100644
index 000000000..b3f075ac8
--- /dev/null
+++ b/.changeset/wise-houses-work.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/adapter-static': patch
+---
+
+Tweak fallback page README
diff --git a/.changeset/yellow-apricots-stare.md b/.changeset/yellow-apricots-stare.md
new file mode 100644
index 000000000..ccdaccc1c
--- /dev/null
+++ b/.changeset/yellow-apricots-stare.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] deduplicate injected css during dev
diff --git a/.changeset/yellow-eagles-leave.md b/.changeset/yellow-eagles-leave.md
new file mode 100644
index 000000000..0dfa6a9bf
--- /dev/null
+++ b/.changeset/yellow-eagles-leave.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Set default options for dev command
diff --git a/documentation/docs/07-service-workers.md b/documentation/docs/07-service-workers.md
index 539a46f57..ad34a7cac 100644
--- a/documentation/docs/07-service-workers.md
+++ b/documentation/docs/07-service-workers.md
@@ -6,7 +6,7 @@ Service workers act as proxy servers that handle network requests inside your ap
 
 In SvelteKit, if you have a `src/service-worker.js` file (or `src/service-worker.ts`, or `src/service-worker/index.js`, etc) it will be built with Vite and automatically registered. You can disable automatic registration if you need to register the service worker with your own logic (e.g. prompt user for update, configure periodic updates, use `workbox`, etc).
 
-> You can change the location of your service worker and disable automatic registration in your [project configuration](/docs/configuration#files).
+> You can change the [location of your service worker](/docs/configuration#files) and [disable automatic registration](/docs/configuration#serviceworker) in your project configuration.
 
 Inside the service worker you have access to the [`$service-worker` module](/docs/modules#$service-worker).
 
diff --git a/packages/adapter-auto/CHANGELOG.md b/packages/adapter-auto/CHANGELOG.md
index 909c884e0..413b88636 100644
--- a/packages/adapter-auto/CHANGELOG.md
+++ b/packages/adapter-auto/CHANGELOG.md
@@ -1,5 +1,13 @@
 # @sveltejs/adapter-auto
 
+## 1.0.0-next.43
+
+### Patch Changes
+
+- Updated dependencies [[`0efa319d`](https://github.com/sveltejs/kit/commit/0efa319d0a03c180a66845d1a4933589084ddb48), [`3d34924c`](https://github.com/sveltejs/kit/commit/3d34924c3f3c1af75c326c6d92cdbfb263f0492a), [`e685c5ae`](https://github.com/sveltejs/kit/commit/e685c5aecdefbc57fcbec5624ed43fd7dd01a037)]:
+  - @sveltejs/adapter-netlify@1.0.0-next.58
+  - @sveltejs/adapter-vercel@1.0.0-next.52
+
 ## 1.0.0-next.42
 
 ### Patch Changes
diff --git a/packages/adapter-auto/package.json b/packages/adapter-auto/package.json
index 3f850a833..bc425d7dc 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.42",
+	"version": "1.0.0-next.43",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/adapter-netlify/CHANGELOG.md b/packages/adapter-netlify/CHANGELOG.md
index 8da702a72..c750a894a 100644
--- a/packages/adapter-netlify/CHANGELOG.md
+++ b/packages/adapter-netlify/CHANGELOG.md
@@ -1,5 +1,11 @@
 # @sveltejs/adapter-netlify
 
+## 1.0.0-next.58
+
+### Patch Changes
+
+- [docs] explain how to change node version ([#4921](https://github.com/sveltejs/kit/pull/4921))
+
 ## 1.0.0-next.57
 
 ### Patch Changes
diff --git a/packages/adapter-netlify/package.json b/packages/adapter-netlify/package.json
index 076d21a2a..f2a649c90 100644
--- a/packages/adapter-netlify/package.json
+++ b/packages/adapter-netlify/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/adapter-netlify",
-	"version": "1.0.0-next.57",
+	"version": "1.0.0-next.58",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/adapter-static/CHANGELOG.md b/packages/adapter-static/CHANGELOG.md
index 70cac6d64..af44501b4 100644
--- a/packages/adapter-static/CHANGELOG.md
+++ b/packages/adapter-static/CHANGELOG.md
@@ -1,5 +1,11 @@
 # @sveltejs/adapter-static
 
+## 1.0.0-next.31
+
+### Patch Changes
+
+- Tweak fallback page README ([#4954](https://github.com/sveltejs/kit/pull/4954))
+
 ## 1.0.0-next.30
 
 ### Patch Changes
diff --git a/packages/adapter-static/README.md b/packages/adapter-static/README.md
index e1feb3f71..9644e0e11 100644
--- a/packages/adapter-static/README.md
+++ b/packages/adapter-static/README.md
@@ -54,7 +54,7 @@ You can use `adapter-static` to create a single-page app or SPA by specifying a
 
 > In most situations this is not recommended: it harms SEO, tends to slow down perceived performance, and makes your app inaccessible to users if JavaScript fails or is disabled (which happens [more often than you probably think](https://kryogenix.org/code/browser/everyonehasjs.html)).
 
-The fallback page is a blank HTML page that loads your SvelteKit app and navigates to the correct route. For example [Surge](https://surge.sh/help/adding-a-200-page-for-client-side-routing), a static web host, lets you add a `200.html` file that will handle any requests that don't otherwise match. We can create that file like so:
+The fallback page is an HTML page created by SvelteKit that loads your app and navigates to the correct route. For example [Surge](https://surge.sh/help/adding-a-200-page-for-client-side-routing), a static web host, lets you add a `200.html` file that will handle any requests that don't otherwise match. We can create that file like so:
 
 ```js
 // svelte.config.js
diff --git a/packages/adapter-static/package.json b/packages/adapter-static/package.json
index 05cafdb44..cd2da4594 100644
--- a/packages/adapter-static/package.json
+++ b/packages/adapter-static/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/adapter-static",
-	"version": "1.0.0-next.30",
+	"version": "1.0.0-next.31",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/adapter-vercel/CHANGELOG.md b/packages/adapter-vercel/CHANGELOG.md
index 53239671d..2c8745ba4 100644
--- a/packages/adapter-vercel/CHANGELOG.md
+++ b/packages/adapter-vercel/CHANGELOG.md
@@ -1,5 +1,13 @@
 # @sveltejs/adapter-vercel
 
+## 1.0.0-next.52
+
+### Patch Changes
+
+- [breaking] drop Node 14 support ([#4922](https://github.com/sveltejs/kit/pull/4922))
+
+* Use general purpose Edge Functions instead of piggybacking Middleware for Edge Deployment + fix split mode ([#4883](https://github.com/sveltejs/kit/pull/4883))
+
 ## 1.0.0-next.51
 
 ### Patch Changes
diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js
index 0cd43b209..6c89940d8 100644
--- a/packages/adapter-vercel/index.js
+++ b/packages/adapter-vercel/index.js
@@ -329,7 +329,7 @@ async function v3(builder, external, edge, split) {
 			})
 		);
 
-		routes.push({ src: pattern, middlewarePath: name });
+		routes.push({ src: pattern, dest: `/${name}` });
 	}
 
 	const generate_function = edge ? generate_edge_function : generate_serverless_function;
@@ -340,10 +340,19 @@ async function v3(builder, external, edge, split) {
 				id: route.pattern.toString(), // TODO is `id` necessary?
 				filter: (other) => route.pattern.toString() === other.pattern.toString(),
 				complete: async (entry) => {
-					const src = `${route.pattern
+					let sliced_pattern = route.pattern
 						.toString()
-						.slice(1, -2) // remove leading / and trailing $/
-						.replace(/\\\//g, '/')}(?:/__data.json)?$`; // TODO adding /__data.json is a temporary workaround — those endpoints should be treated as distinct routes
+						// remove leading / and trailing $/
+						.slice(1, -2)
+						// replace escaped \/ with /
+						.replace(/\\\//g, '/');
+
+					// replace the root route "^/" with "^/?"
+					if (sliced_pattern === '^/') {
+						sliced_pattern = '^/?';
+					}
+
+					const src = `${sliced_pattern}(?:/__data.json)?$`; // TODO adding /__data.json is a temporary workaround — those endpoints should be treated as distinct routes
 
 					await generate_function(route.id || 'index', src, entry.generateManifest);
 				}
diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json
index 480abcc1b..16ff0b334 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.51",
+	"version": "1.0.0-next.52",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/create-svelte/CHANGELOG.md b/packages/create-svelte/CHANGELOG.md
index 051c544bb..fc258c9a2 100644
--- a/packages/create-svelte/CHANGELOG.md
+++ b/packages/create-svelte/CHANGELOG.md
@@ -1,5 +1,11 @@
 # create-svelte
 
+## 2.0.0-next.136
+
+### Patch Changes
+
+- Add README ([#4951](https://github.com/sveltejs/kit/pull/4951))
+
 ## 2.0.0-next.135
 
 ### Patch Changes
diff --git a/packages/create-svelte/README.md b/packages/create-svelte/README.md
new file mode 100644
index 000000000..c103fac15
--- /dev/null
+++ b/packages/create-svelte/README.md
@@ -0,0 +1,32 @@
+# create-svelte
+
+A CLI for creating new [SvelteKit](https://kit.svelte.dev) projects. Just run...
+
+```bash
+npm init svelte
+```
+
+...and follow the prompts.
+
+## API
+
+You can also use `create-svelte` programmatically:
+
+```js
+import { create } from 'create-svelte';
+
+await create('my-new-app', {
+  name: 'my-new-app',
+  template: 'default', // or 'skeleton'
+  types: 'checkjs', // or 'typescript' or null;
+  prettier: false,
+  eslint: false,
+  playwright: false
+});
+```
+
+`checkjs` means your project will use TypeScript to typecheck JavaScript via [JSDoc comments](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html).
+
+## License
+
+[MIT](../../LICENSE).
diff --git a/packages/create-svelte/package.json b/packages/create-svelte/package.json
index 2a02efa7b..7c4c7a9b5 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.135",
+	"version": "2.0.0-next.136",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index 50bd1df15..9d3d36e39 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,29 @@
 # @sveltejs/kit
 
+## 1.0.0-next.333
+
+### Patch Changes
+
+- [breaking] delay automatic service worker registration until load event ([#4950](https://github.com/sveltejs/kit/pull/4950))
+
+## 1.0.0-next.332
+
+### Patch Changes
+
+- Revert dev command default options ([#4949](https://github.com/sveltejs/kit/pull/4949))
+
+## 1.0.0-next.331
+
+### Patch Changes
+
+- fix `BodyValidator` for nested interfaces ([#4939](https://github.com/sveltejs/kit/pull/4939))
+
+* [breaking] drop Node 14 support ([#4922](https://github.com/sveltejs/kit/pull/4922))
+
+- only skip files that were already written when prerendering ([#4928](https://github.com/sveltejs/kit/pull/4928))
+
+* Set default options for dev command ([#4932](https://github.com/sveltejs/kit/pull/4932))
+
 ## 1.0.0-next.330
 
 ### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index 592358745..9334e29da 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.0.0-next.330",
+	"version": "1.0.0-next.333",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/kit/src/core/dev/index.js b/packages/kit/src/core/dev/index.js
index 14e35c234..f0a261619 100644
--- a/packages/kit/src/core/dev/index.js
+++ b/packages/kit/src/core/dev/index.js
@@ -35,7 +35,8 @@ export async function dev({ cwd, port, host, https, config }) {
 							path.resolve(cwd, 'node_modules'),
 							path.resolve(vite.searchForWorkspaceRoot(cwd), 'node_modules')
 						])
-					]
+					],
+					port: 3000
 				},
 				strictPort: true
 			}
diff --git a/packages/kit/src/core/dev/plugin.js b/packages/kit/src/core/dev/plugin.js
index c21b6a176..a5269fa77 100644
--- a/packages/kit/src/core/dev/plugin.js
+++ b/packages/kit/src/core/dev/plugin.js
@@ -73,7 +73,7 @@ export async function create_plugin(config, cwd) {
 								if (!node) throw new Error(`Could not find node for ${url}`);
 
 								const deps = new Set();
-								find_deps(node, deps);
+								await find_deps(vite, node, deps);
 
 								/** @type {Record<string, string>} */
 								const styles = {};
@@ -387,14 +387,36 @@ function remove_html_middlewares(server) {
 }
 
 /**
+ * @param {import('vite').ViteDevServer} vite
  * @param {import('vite').ModuleNode} node
  * @param {Set<import('vite').ModuleNode>} deps
  */
-function find_deps(node, deps) {
-	for (const dep of node.importedModules) {
-		if (!deps.has(dep)) {
-			deps.add(dep);
-			find_deps(dep, deps);
+function find_deps(vite, node, deps) {
+	// since `ssrTransformResult.deps` contains URLs instead of `ModuleNode`s, this process is asynchronous.
+	// instead of using `await`, we resolve all branches in parallel.
+	/** @type {Promise<void>[]} */
+	const branches = [];
+
+	/** @param {import('vite').ModuleNode} node */
+	function add(node) {
+		if (!deps.has(node)) {
+			deps.add(node);
+			branches.push(find_deps(vite, node, deps));
 		}
 	}
+
+	/** @param {string} url */
+	async function add_by_url(url) {
+		branches.push(vite.moduleGraph.getModuleByUrl(url).then((node) => node && add(node)));
+	}
+
+	if (node.ssrTransformResult) {
+		if (node.ssrTransformResult.deps) {
+			node.ssrTransformResult.deps.forEach(add_by_url);
+		}
+	} else {
+		node.importedModules.forEach(add);
+	}
+
+	return Promise.all(branches).then(() => {});
 }
diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js
index b7ad8f862..269c7fc6b 100644
--- a/packages/kit/src/runtime/server/page/render.js
+++ b/packages/kit/src/runtime/server/page/render.js
@@ -182,7 +182,9 @@ export async function render_response({
 
 	const init_service_worker = `
 		if ('serviceWorker' in navigator) {
-			navigator.serviceWorker.register('${options.service_worker}');
+			addEventListener('load', () => {
+				navigator.serviceWorker.register('${options.service_worker}');
+			});
 		}
 	`;
 
diff --git a/packages/kit/types/private.d.ts b/packages/kit/types/private.d.ts
index ddf476d80..685243968 100644
--- a/packages/kit/types/private.d.ts
+++ b/packages/kit/types/private.d.ts
@@ -27,7 +27,11 @@ export interface AdapterEntry {
 }
 
 export type BodyValidator<T> = {
-	[P in keyof T]: T[P] extends JSONValue ? BodyValidator<T[P]> : never;
+	[P in keyof T]: T[P] extends { [k: string]: infer V }
+		? BodyValidator<V> // recurse when T[P] is an object
+		: T[P] extends BigInt | Function | Symbol
+		? never
+		: T[P];
 };
 
 // Based on https://github.com/josh-hemphill/csp-typed-directives/blob/latest/src/csp.types.ts


Test patch:
diff --git a/packages/kit/test/apps/basics/src/routes/css/_base.css b/packages/kit/test/apps/basics/src/routes/css/_base.css
new file mode 100644
index 000000000..82bb61cfe
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/css/_base.css
@@ -0,0 +1,3 @@
+.overridden {
+	color: red;
+}
diff --git a/packages/kit/test/apps/basics/src/routes/css/_styles.css b/packages/kit/test/apps/basics/src/routes/css/_styles.css
index 29c4b1668..78548a9f3 100644
--- a/packages/kit/test/apps/basics/src/routes/css/_styles.css
+++ b/packages/kit/test/apps/basics/src/routes/css/_styles.css
@@ -1,3 +1,9 @@
+@import '_base.css';
+
 .styled {
 	color: red;
 }
+
+.overridden {
+	color: green;
+}
diff --git a/packages/kit/test/apps/basics/src/routes/css/index.svelte b/packages/kit/test/apps/basics/src/routes/css/index.svelte
index 9bbef6fba..45074c38b 100644
--- a/packages/kit/test/apps/basics/src/routes/css/index.svelte
+++ b/packages/kit/test/apps/basics/src/routes/css/index.svelte
@@ -10,10 +10,14 @@
 	this text is blue
 </div>
 
+<div class="overridden">
+	this text is green
+</div>
+
 <a href="/css/other">other</a>
 
 <style>
 	.also-styled {
 		color: blue;
 	}
-</style>
\ No newline at end of file
+</style>
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js
index 3e56c2de8..cf7086835 100644
--- a/packages/kit/test/apps/basics/test/test.js
+++ b/packages/kit/test/apps/basics/test/test.js
@@ -474,6 +474,13 @@ test.describe.parallel('CSS', () => {
 			).toBe('absolute');
 		}
 	});
+
+	test('applies imported styles in the correct order', async ({ page }) => {
+		await page.goto('/css');
+
+		const color = await page.$eval('.overridden', (el) => getComputedStyle(el).color);
+		expect(color).toBe('rgb(0, 128, 0)');
+	});
 });
 
 test.describe.parallel('Shadowed pages', () => {
diff --git a/packages/kit/test/typings/endpoint.test.ts b/packages/kit/test/typings/endpoint.test.ts
index 4fa6cb2a7..6266c02a1 100644
--- a/packages/kit/test/typings/endpoint.test.ts
+++ b/packages/kit/test/typings/endpoint.test.ts
@@ -92,14 +92,62 @@ export const generic_case: RequestHandler<Record<string, string>, ExamplePost> =
 	};
 };
 
+interface NestedChild {
+	message: string;
+}
+interface ParentWrapper {
+	fields: NestedChild;
+}
+export const nested_interfaces: RequestHandler<Record<string, string>, ParentWrapper> = () => {
+	return {
+		body: {} as ParentWrapper
+	};
+};
+
 // --- invalid cases ---
 
-// @ts-expect-error - body must be JSON serializable
-export const error_unserializable_literal: RequestHandler = () => {
+// @ts-expect-error - bigint cannot be converted to JSON string
+export const error_unserializable_literal_bigint: RequestHandler = () => {
+	return {
+		body: BigInt('')
+	};
+};
+// @ts-expect-error - bigint cannot be converted to JSON string
+export const error_unserializable_property_bigint: RequestHandler = () => {
+	return {
+		body: {
+			answer: BigInt('')
+		}
+	};
+};
+// @ts-expect-error - function cannot be converted to JSON string
+export const error_unserializable_literal_function: RequestHandler = () => {
 	return {
 		body: () => {}
 	};
 };
+// @ts-expect-error - function cannot be converted to JSON string
+export const error_unserializable_property_function: RequestHandler = () => {
+	return {
+		body: {
+			sorter() {}
+		}
+	};
+};
+// @ts-expect-error - symbol cannot be converted to JSON string
+export const error_unserializable_literal_symbol: RequestHandler = () => {
+	return {
+		body: Symbol()
+	};
+};
+// @ts-expect-error - symbol cannot be converted to JSON string
+export const error_unserializable_property_symbol: RequestHandler = () => {
+	return {
+		body: {
+			id: Symbol()
+		}
+	};
+};
 
 /** example object that isn't serializable */
 interface InvalidPost {