Repository: sveltejs/kit
Pull request #7264 (2022-10-14 13:42:38)

[feat] more info about prerendering errors and new strict option
Helps with #7183 and #7244

### 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
- [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/pre.json b/.changeset/pre.json
index caf05c937..efaab784e 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -973,6 +973,7 @@
     "serious-elephants-dress",
     "serious-fireants-drum",
     "serious-horses-rule",
+    "serious-llamas-smoke",
     "serious-panthers-sing",
     "serious-vans-arrive",
     "seven-ads-yawn",
@@ -1131,6 +1132,7 @@
     "spotty-countries-protect",
     "spotty-dancers-clean",
     "spotty-dragons-wait",
+    "spotty-kangaroos-peel",
     "spotty-ladybugs-teach",
     "spotty-parents-love",
     "spotty-phones-love",
@@ -1386,6 +1388,7 @@
     "wicked-spoons-leave",
     "wicked-terms-agree",
     "wild-badgers-brake",
+    "wild-bags-cheat",
     "wild-beans-happen",
     "wild-clouds-hear",
     "wild-cooks-drive",
diff --git a/.changeset/serious-llamas-smoke.md b/.changeset/serious-llamas-smoke.md
new file mode 100644
index 000000000..7a33ae3e3
--- /dev/null
+++ b/.changeset/serious-llamas-smoke.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] more informative serialization error messages
diff --git a/.changeset/spotty-kangaroos-peel.md b/.changeset/spotty-kangaroos-peel.md
new file mode 100644
index 000000000..db342321a
--- /dev/null
+++ b/.changeset/spotty-kangaroos-peel.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] add `$types` to includes for better DX
diff --git a/.changeset/tame-bats-tell.md b/.changeset/tame-bats-tell.md
new file mode 100644
index 000000000..9480534b0
--- /dev/null
+++ b/.changeset/tame-bats-tell.md
@@ -0,0 +1,6 @@
+---
+'@sveltejs/adapter-static': patch
+'@sveltejs/kit': patch
+---
+
+[feat] more info about prerendering errors, add strict option to adapter-static
diff --git a/.changeset/wild-bags-cheat.md b/.changeset/wild-bags-cheat.md
new file mode 100644
index 000000000..fbe87d045
--- /dev/null
+++ b/.changeset/wild-bags-cheat.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+Warn user when they accidentally try to publish the `./` directory
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6e80c34cd..9e9ed7f03 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,7 +25,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v3
-      - uses: pnpm/action-setup@v2.2.3
+      - uses: pnpm/action-setup@v2.2.4
       - uses: actions/setup-node@v3
         with:
           node-version: '16.x'
@@ -58,7 +58,7 @@ jobs:
     steps:
       - run: git config --global core.autocrlf false
       - uses: actions/checkout@v3
-      - uses: pnpm/action-setup@v2.2.3
+      - uses: pnpm/action-setup@v2.2.4
       - uses: actions/setup-node@v3
         with:
           node-version: ${{ matrix.node-version }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 173f7dc6e..2b06d36a4 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -26,7 +26,7 @@ jobs:
         with:
           # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
           fetch-depth: 0
-      - uses: pnpm/action-setup@v2.2.3
+      - uses: pnpm/action-setup@v2.2.4
       - name: Setup Node.js
         uses: actions/setup-node@v3
         with:
diff --git a/documentation/docs/12-adapters.md b/documentation/docs/12-adapters.md
index aae3baa68..860b1d51d 100644
--- a/documentation/docs/12-adapters.md
+++ b/documentation/docs/12-adapters.md
@@ -52,7 +52,7 @@ Most adapters will generate static HTML for any [prerenderable](/docs/page-optio
 +import adapter from '@sveltejs/adapter-static';
 ```
 
-You can also use `adapter-static` to generate single-page apps (SPAs) by specifying a [fallback page](https://github.com/sveltejs/kit/tree/master/packages/adapter-static#spa-mode).
+You can also use `adapter-static` to generate single-page apps (SPAs) by specifying a [fallback page and disabling SSR](https://github.com/sveltejs/kit/tree/master/packages/adapter-static#spa-mode).
 
 > You must ensure [`trailingSlash`](/docs/configuration#trailingslash) is set appropriately for your environment. If your host does not render `/a.html` upon receiving a request for `/a` then you will need to set `trailingSlash: 'always'` to create `/a/index.html` instead.
 
diff --git a/documentation/docs/13-page-options.md b/documentation/docs/13-page-options.md
index c9dfaef92..3ea49fe43 100644
--- a/documentation/docs/13-page-options.md
+++ b/documentation/docs/13-page-options.md
@@ -6,6 +6,8 @@ By default, SvelteKit will render (or [prerender](/docs/appendix#prerendering))
 
 You can control each of these on a page-by-page basis by exporting options from [`+page.js`](/docs/routing#page-page-js) or [`+page.server.js`](/docs/routing#page-page-server-js), or for groups of pages using a shared [`+layout.js`](/docs/routing#layout-layout-js) or [`+layout.server.js`](/docs/routing#layout-layout-server-js). To define an option for the whole app, export it from the root layout. Child layouts and pages override values set in parent layouts, so — for example — you can enable prerendering for your entire app then disable it for pages that need to be dynamically rendered.
 
+You can mix and match these options in different areas of your app. For example you could prerender your marketing page for maximum speed, server-render your dynamic pages for SEO and accessibility and turn your admin section into an SPA by rendering it on the client only. This makes SvelteKit very versatile.
+
 ### prerender
 
 It's likely that at least some routes of your app can be represented as a simple HTML file generated at build time. These routes can be [_prerendered_](/docs/appendix#prerendering).
@@ -72,22 +74,24 @@ Note that this will disable client-side routing for any navigation from this pag
 
 #### Troubleshooting
 
-If you encounter an error like 'The following routes were marked as prerenderable, but were not prerendered' it's because the route in question (or a parent layout, if it's a page) has `export const prerender = true` but the page wasn't actually prerendered.
+If you encounter an error like 'The following routes were marked as prerenderable, but were not prerendered' it's because the route in question (or a parent layout, if it's a page) has `export const prerender = true` but the page wasn't actually prerendered, because it wasn't reached by the prerendering crawler.
 
 Since these routes cannot be dynamically server-rendered, this will cause errors when people try to access the route in question. There are two ways to fix it:
 
-* Ensure that SvelteKit can find the route by following links from [`config.kit.prerender.entries`](/docs/configuration#prerender). The pages containing the links (e.g. your `/` page) must _themselves_ be prerenderable, or they will be ignored
+* Ensure that SvelteKit can find the route by following links from [`config.kit.prerender.entries`](/docs/configuration#prerender). Add links to dynamic routes (i.e. pages with `[parameters]` ) to this option if they are not found through crawling the other entry points, else they are not prerendered because SvelteKit doesn't know what value the parameters should have. Pages not marked as prerenderable will be ignored and their links to other pages will not be crawled, even if some of them would be prerenderable.
 * Change `export const prerender = true` to `export const prerender = 'auto'`. Routes with `'auto'` can be dynamically server rendered
 
 ### ssr
 
-Normally, SvelteKit renders your page on the server first and sends that HTML to the client where it's hydrated. If you set `ssr` to `false`, it renders an empty 'shell' page instead. This is useful if your page is unable to be rendered on the server, but in most situations it's not recommended ([see appendix](/docs/appendix#ssr)).
+Normally, SvelteKit renders your page on the server first and sends that HTML to the client where it's hydrated. If you set `ssr` to `false`, it renders an empty 'shell' page instead. This is useful if your page is unable to be rendered on the server (because you use browser-only globals like `document` for example), but in most situations it's not recommended ([see appendix](/docs/appendix#ssr)).
 
 ```js
 /// file: +page.js
 export const ssr = false;
 ```
 
+If you add `export const ssr = false` to your root `+layout.js`, your entire app will only be rendered on the client — which essentially means you turn your app into an SPA.
+
 ### csr
 
 Ordinarily, SvelteKit [hydrates](/docs/appendix#hydration) your server-rendered HTML into an interactive client-side-rendered (CSR) page. Some pages don't require JavaScript at all — many blog posts and 'about' pages fall into this category. In these cases you can disable CSR:
diff --git a/documentation/docs/16-configuration.md b/documentation/docs/16-configuration.md
index 0b9319dfd..e5f145453 100644
--- a/documentation/docs/16-configuration.md
+++ b/documentation/docs/16-configuration.md
@@ -252,7 +252,7 @@ See [Prerendering](/docs/page-options#prerender). An object containing zero or m
 - `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)
 - `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]` )
+- `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]`, because SvelteKit doesn't know what value the parameters should have)
 - `onError`
 
   - `'fail'` — (default) fails the build when a routing error is encountered when following a link
diff --git a/package.json b/package.json
index ed3dd0472..fa163a1b6 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
 		"turbo": "^1.3.4",
 		"typescript": "^4.8.2"
 	},
-	"packageManager": "pnpm@7.13.4",
+	"packageManager": "pnpm@7.13.5",
 	"engines": {
 		"pnpm": "^7.0.0"
 	},
diff --git a/packages/adapter-static/README.md b/packages/adapter-static/README.md
index bd90b5a2d..540239ae8 100644
--- a/packages/adapter-static/README.md
+++ b/packages/adapter-static/README.md
@@ -18,7 +18,8 @@ export default {
       pages: 'build',
       assets: 'build',
       fallback: null,
-      precompress: false
+      precompress: false,
+      strict: true
     })
   }
 };
@@ -71,13 +72,17 @@ Specify a fallback page for SPA mode, e.g. `index.html` or `200.html` or `404.ht
 
 If `true`, precompresses files with brotli and gzip. This will generate `.br` and `.gz` files.
 
+### strict
+
+By default, `adapter-static` checks that either all pages and endpoints (if any) of your app were prerendered, or you have the `fallback` option set. This check exists to prevent you from accidentally publishing an app where some parts of it are not accessible, because they are not contained in the final output. If you know this is ok (for example when a certain page only exists conditionally), you can set `strict` to `false` to turn off this check.
+
 ## SPA mode
 
 You can use `adapter-static` to create a single-page app or SPA by specifying a **fallback page**.
 
 > 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 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 correspond to static assets or prerendered pages. We can create that file like so:
+If you want to create a simple SPA with no prerendered routes, the necessary config looks like this:
 
 ```js
 // svelte.config.js
@@ -87,20 +92,24 @@ export default {
   kit: {
     adapter: adapter({
       fallback: '200.html'
-    })
+    }),
+    prerender: { entries: [] }
   }
 };
 ```
 
-> How to configure this behaviour does however depend on your hosting solution and is not part of SvelteKit. It is recommended to search the host's documentation on how to redirect requests.
+```js
+// src/routes/+layout.js
+export const ssr = false;
+```
 
-When operating in SPA mode, you can omit the [`prerender`](https://kit.svelte.dev/docs/page-options#prerender) option from your root layout (or set it to `false`, its default value), and only pages that have the `prerender` option set will be prerendered at build time.
+You can also turn only part of your app into an SPA.
 
-SvelteKit will still crawl your app's entry points looking for prerenderable pages. If `svelte-kit build` fails because of pages that can't be loaded outside the browser, you can set `config.kit.prerender.entries` to `[]` to prevent this from happening. (Setting `config.kit.prerender.enabled` to `false` also has this effect, but would prevent the fallback page from being generated.)
+Let's go through these options one by one:
 
-During development, SvelteKit will still attempt to server-side render your routes. This means accessing things that are only available in the browser (such as the `window` object) will result in errors, even though this would be valid in the output app. To align the behavior of SvelteKit's dev mode with your SPA, you can [add `export const ssr = false` to your root `+layout`](https://kit.svelte.dev/docs/page-options#ssr).
+### Add fallback page
 
-If you want to create a simple SPA with no prerendered routes, the necessary config therefore looks like this:
+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 correspond to static assets or prerendered pages. We can create that file like so:
 
 ```js
 // svelte.config.js
@@ -110,16 +119,24 @@ export default {
   kit: {
     adapter: adapter({
       fallback: '200.html'
-    }),
-    prerender: { entries: [] }
+    })
   }
 };
 ```
 
-```js
-// src/routes/+layout.js
-export const ssr = false;
-```
+> How to configure this behaviour does however depend on your hosting solution and is not part of SvelteKit. It is recommended to search the host's documentation on how to redirect requests.
+
+### Turn off prerendering
+
+When operating in SPA mode, you can omit the [`prerender`](https://kit.svelte.dev/docs/page-options#prerender) option from your root layout (or set it to `false`, its default value), and only pages that have the `prerender` option set will be prerendered at build time.
+
+SvelteKit will still crawl your app's entry points looking for prerenderable pages. If `svelte-kit build` fails because of pages that can't be loaded outside the browser, you can set `config.kit.prerender.entries` to `[]` to prevent this from happening. (Setting `config.kit.prerender.enabled` to `false` also has this effect, but would prevent the fallback page from being generated.)
+
+You can also add turn off prerendering only to parts of your app, if you want other parts to be prerendered.
+
+### Turn off ssr
+
+During development, SvelteKit will still attempt to server-side render your routes. This means accessing things that are only available in the browser (such as the `window` object) will result in errors, even though this would be valid in the output app. To align the behavior of SvelteKit's dev mode with your SPA, you can [add `export const ssr = false` to your root `+layout`](https://kit.svelte.dev/docs/page-options#ssr). You can also add this option only to parts of your app, if you want other parts to be prerendered.
 
 ## GitHub Pages
 
diff --git a/packages/adapter-static/index.d.ts b/packages/adapter-static/index.d.ts
index a85449101..101e02fde 100644
--- a/packages/adapter-static/index.d.ts
+++ b/packages/adapter-static/index.d.ts
@@ -5,6 +5,7 @@ export interface AdapterOptions {
 	assets?: string;
 	fallback?: string;
 	precompress?: boolean;
+	strict?: boolean;
 }
 
 export default function plugin(options?: AdapterOptions): Adapter;
diff --git a/packages/adapter-static/index.js b/packages/adapter-static/index.js
index a32a8010b..37c3f8538 100644
--- a/packages/adapter-static/index.js
+++ b/packages/adapter-static/index.js
@@ -24,13 +24,31 @@ export default function (options) {
 					};
 				});
 
-				if (dynamic_routes.length > 0) {
+				if (dynamic_routes.length > 0 && options.strict) {
 					const prefix = path.relative('.', builder.config.kit.files.routes);
+					const has_param_routes = dynamic_routes.some((route) => route.includes('['));
+					const config_option =
+						has_param_routes || JSON.stringify(builder.config.kit.prerender.entries) !== '["*"]'
+							? `  - adjust the \`prerender.entries\` config option ${
+									has_param_routes
+										? '(routes with parameters are not part of entry points by default)'
+										: ''
+							  } — see https://kit.svelte.dev/docs/configuration#prerender for more info.`
+							: '';
+
 					builder.log.error(
-						`@sveltejs/adapter-static: all routes must be fully prerenderable (unless using the 'fallback' option — see https://github.com/sveltejs/kit/tree/master/packages/adapter-static#spa-mode). Try adding \`export const prerender = true\` to your root +layout.js/.ts file — see https://kit.svelte.dev/docs/page-options#prerender for more details`
-					);
-					builder.log.error(
-						dynamic_routes.map((id) => `  - ${path.posix.join(prefix, id)}`).join('\n')
+						`@sveltejs/adapter-static: all routes must be fully prerenderable, but found the following routes that are dynamic:
+${dynamic_routes.map((id) => `  - ${path.posix.join(prefix, id)}`).join('\n')}
+
+You have the following options:
+  - set the \`fallback\` option — see https://github.com/sveltejs/kit/tree/master/packages/adapter-static#spa-mode for more info.
+  - add \`export const prerender = true\` to your root \`+layout.js/.ts\` or \`+layout.server.js/.ts\` file. This will try to prerender all pages.
+  - add \`export const prerender = true\` to any \`+server.js/ts\` files that are not fetched by page \`load\` functions.
+${config_option}
+  - pass \`strict: false\` to \`adapter-static\` to ignore this error. Only do this if you are sure you don't need the routes in question in your final app, as they will be unavailable. See https://github.com/sveltejs/kit/tree/master/packages/adapter-static#strict for more info.
+
+If this doesn't help, you may need to use a different adapter. @sveltejs/adapter-static can only be used for sites that don't need a server for dynamic rendering, and can run on just a static file server.
+See https://kit.svelte.dev/docs/page-options#prerender for more details`
 					);
 					throw new Error('Encountered dynamic routes');
 				}
diff --git a/packages/create-svelte/CHANGELOG.md b/packages/create-svelte/CHANGELOG.md
index 358935e3e..d1a0146e0 100644
--- a/packages/create-svelte/CHANGELOG.md
+++ b/packages/create-svelte/CHANGELOG.md
@@ -1,5 +1,11 @@
 # create-svelte
 
+## 2.0.0-next.185
+
+### Patch Changes
+
+- Warn user when they accidentally try to publish the `./` directory ([#7280](https://github.com/sveltejs/kit/pull/7280))
+
 ## 2.0.0-next.184
 
 ### Patch Changes
diff --git a/packages/create-svelte/package.json b/packages/create-svelte/package.json
index 81e0e3b09..13a377fc5 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.184",
+	"version": "2.0.0-next.185",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/create-svelte/templates/skeletonlib/package.template.json b/packages/create-svelte/templates/skeletonlib/package.template.json
index 6b90688f1..78e1bbf16 100644
--- a/packages/create-svelte/templates/skeletonlib/package.template.json
+++ b/packages/create-svelte/templates/skeletonlib/package.template.json
@@ -3,7 +3,8 @@
 	"version": "0.0.1",
 	"scripts": {
 		"dev": "vite dev",
-		"build": "svelte-kit sync && svelte-package"
+		"build": "svelte-kit sync && svelte-package",
+		"prepublishOnly": "echo 'Did you mean to publish `./package/`, instead of `./`?' && exit 1"
 	},
 	"devDependencies": {
 		"@sveltejs/adapter-auto": "workspace:*",
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index 4f35edc13..8775815b0 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,13 @@
 # @sveltejs/kit
 
+## 1.0.0-next.517
+
+### Patch Changes
+
+- [fix] more informative serialization error messages ([#7303](https://github.com/sveltejs/kit/pull/7303))
+
+* [fix] add `$types` to includes for better DX ([#7301](https://github.com/sveltejs/kit/pull/7301))
+
 ## 1.0.0-next.516
 
 ### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index 97d2e39a3..224d084ad 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.0.0-next.516",
+	"version": "1.0.0-next.517",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js
index 3c1dc0bcd..c95921a4b 100644
--- a/packages/kit/src/core/adapt/builder.js
+++ b/packages/kit/src/core/adapt/builder.js
@@ -33,10 +33,11 @@ export function create_builder({ config, build_data, routes, prerendered, log })
 		async createEntries(fn) {
 			/** @type {import('types').RouteDefinition[]} */
 			const facades = routes.map((route) => {
+				/** @type {Set<import('types').HttpMethod>} */
 				const methods = new Set();
 
 				if (route.page) {
-					methods.add('SET');
+					methods.add('GET');
 				}
 
 				if (route.endpoint) {
diff --git a/packages/kit/src/core/prerender/prerender.js b/packages/kit/src/core/prerender/prerender.js
index 65d755c13..40d04eb4d 100644
--- a/packages/kit/src/core/prerender/prerender.js
+++ b/packages/kit/src/core/prerender/prerender.js
@@ -431,9 +431,9 @@ export async function prerender() {
 
 	if (not_prerendered.length > 0) {
 		throw new Error(
-			`The following routes were marked as prerenderable, but were not prerendered:\n${not_prerendered.map(
+			`The following routes were marked as prerenderable, but were not prerendered because they were not found while crawling your app:\n${not_prerendered.map(
 				(id) => `  - ${id}`
-			)}\n\nSee https://kit.svelte.dev/docs/page-options#prerender-troubleshooting for more info`
+			)}\n\nSee https://kit.svelte.dev/docs/page-options#prerender-troubleshooting for info on how to solve this`
 		);
 	}
 
diff --git a/packages/kit/src/core/sync/write_tsconfig.js b/packages/kit/src/core/sync/write_tsconfig.js
index 8e3d42ebd..b97b1a084 100644
--- a/packages/kit/src/core/sync/write_tsconfig.js
+++ b/packages/kit/src/core/sync/write_tsconfig.js
@@ -46,7 +46,7 @@ export function write_tsconfig(config, cwd = process.cwd()) {
 	/** @param {string} file */
 	const config_relative = (file) => posixify(path.relative(config.outDir, file));
 
-	const include = ['ambient.d.ts', config_relative('vite.config.ts')];
+	const include = ['ambient.d.ts', './types/**/$types.d.ts', config_relative('vite.config.ts')];
 	for (const dir of [config.files.routes, config.files.lib]) {
 		const relative = project_relative(path.dirname(dir));
 		include.push(config_relative(`${relative}/**/*.js`));
diff --git a/packages/kit/src/runtime/server/data/index.js b/packages/kit/src/runtime/server/data/index.js
index 3e0ea3890..33c9a68a7 100644
--- a/packages/kit/src/runtime/server/data/index.js
+++ b/packages/kit/src/runtime/server/data/index.js
@@ -111,7 +111,7 @@ export async function render_data(event, route, options, state) {
 			nodes: nodes.slice(0, length)
 		};
 
-		return data_response(server_data);
+		return data_response(server_data, event);
 	} catch (e) {
 		const error = normalize_error(e);
 
@@ -122,10 +122,10 @@ export async function render_data(event, route, options, state) {
 				location: error.location
 			};
 
-			return data_response(server_data);
+			return data_response(server_data, event);
 		} else {
 			// TODO make it clearer that this was an unexpected error
-			return data_response(handle_error_and_jsonify(event, options, error));
+			return data_response(handle_error_and_jsonify(event, options, error), event);
 		}
 	}
 }
diff --git a/packages/kit/src/runtime/server/page/actions.js b/packages/kit/src/runtime/server/page/actions.js
index eb5b7c187..1c1d554cc 100644
--- a/packages/kit/src/runtime/server/page/actions.js
+++ b/packages/kit/src/runtime/server/page/actions.js
@@ -248,5 +248,9 @@ function check_serializability(value, id, path) {
 		}
 	}
 
-	throw new Error(`${path} returned from action in ${id} cannot be serialized as JSON`);
+	throw new Error(
+		`${path} returned from action in ${id} cannot be serialized as JSON without losing its original type` +
+			// probably the most common case, so let's give a hint
+			(value instanceof Date ? ' (Date objects are serialized as strings)' : '')
+	);
 }
diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js
index b26c4da88..7953d3301 100644
--- a/packages/kit/src/runtime/server/page/render.js
+++ b/packages/kit/src/runtime/server/page/render.js
@@ -177,7 +177,11 @@ export async function render_response({
 		// function, but it would mean passing more stuff around than we currently do
 		const error = /** @type {any} */ (e);
 		const match = /\[(\d+)\]\.data\.(.+)/.exec(error.path);
-		if (match) throw new Error(`${error.message} (data.${match[2]})`);
+		if (match) {
+			throw new Error(
+				`Data returned from \`load\` while rendering /${event.routeId} is not serializable: ${error.message} (data.${match[2]})`
+			);
+		}
 		throw error;
 	}
 
diff --git a/packages/kit/src/runtime/server/utils.js b/packages/kit/src/runtime/server/utils.js
index 08bc5437d..33ef9b7c0 100644
--- a/packages/kit/src/runtime/server/utils.js
+++ b/packages/kit/src/runtime/server/utils.js
@@ -67,8 +67,11 @@ export function allowed_methods(mod) {
 	return allowed;
 }
 
-/** @param {any} data */
-export function data_response(data) {
+/**
+ * @param {any} data
+ * @param {import('types').RequestEvent} event
+ */
+export function data_response(data, event) {
 	const headers = {
 		'content-type': 'application/json',
 		'cache-control': 'private, no-store'
@@ -79,7 +82,9 @@ export function data_response(data) {
 	} catch (e) {
 		const error = /** @type {any} */ (e);
 		const match = /\[(\d+)\]\.data\.(.+)/.exec(error.path);
-		const message = match ? `${error.message} (data.${match[2]})` : error.message;
+		const message = match
+			? `Data returned from \`load\` while rendering /${event.routeId} is not serializable: ${error.message} (data.${match[2]})`
+			: error.message;
 		return new Response(JSON.stringify(message), { headers, status: 500 });
 	}
 }
diff --git a/packages/kit/src/utils/filesystem.js b/packages/kit/src/utils/filesystem.js
index 8ddcc0c5c..1cd688186 100644
--- a/packages/kit/src/utils/filesystem.js
+++ b/packages/kit/src/utils/filesystem.js
@@ -6,7 +6,12 @@ export function mkdirp(dir) {
 	try {
 		fs.mkdirSync(dir, { recursive: true });
 	} catch (/** @type {any} */ e) {
-		if (e.code === 'EEXIST') return;
+		if (e.code === 'EEXIST') {
+			if (!fs.statSync(dir).isDirectory()) {
+				throw new Error(`Cannot create directory ${dir}, a file already exists at this position`);
+			}
+			return;
+		}
 		throw e;
 	}
 }


Test patch:
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js
index 0588cdc6b..9331e7708 100644
--- a/packages/kit/test/apps/basics/test/test.js
+++ b/packages/kit/test/apps/basics/test/test.js
@@ -308,7 +308,7 @@ test.describe('Shadowed pages', () => {
 
 			expect(await page.textContent('h1')).toBe('500');
 			expect(await page.textContent('#message')).toBe(
-				'This is your custom error page saying: "Cannot stringify arbitrary non-POJOs (data.nope)"'
+				'This is your custom error page saying: "Data returned from `load` while rendering /shadowed/serialization is not serializable: Cannot stringify arbitrary non-POJOs (data.nope)"'
 			);
 		});
 	}
diff --git a/packages/kit/test/build-errors/prerender.spec.js b/packages/kit/test/build-errors/prerender.spec.js
index 3ddf2c352..209a3dd2b 100644
--- a/packages/kit/test/build-errors/prerender.spec.js
+++ b/packages/kit/test/build-errors/prerender.spec.js
@@ -11,7 +11,7 @@ test('prerenderable routes must be prerendered', () => {
 				stdio: 'pipe',
 				timeout: 15000
 			}),
-		/The following routes were marked as prerenderable, but were not prerendered:\r?\n  - \[x\]/gs
+		/The following routes were marked as prerenderable, but were not prerendered because they were not found while crawling your app:\r?\n  - \[x\]/gs
 	);
 });