@sveltejs/kit 1.0.0-next.452 → 1.0.0-next.455

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.452",
3
+ "version": "1.0.0-next.455",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -10,7 +10,7 @@
10
10
  "homepage": "https://kit.svelte.dev",
11
11
  "type": "module",
12
12
  "dependencies": {
13
- "@sveltejs/vite-plugin-svelte": "^1.0.1",
13
+ "@sveltejs/vite-plugin-svelte": "^1.0.3",
14
14
  "cookie": "^0.5.0",
15
15
  "devalue": "^3.1.2",
16
16
  "kleur": "^4.1.4",
@@ -36,7 +36,7 @@
36
36
  "rollup": "^2.78.1",
37
37
  "svelte": "^3.48.0",
38
38
  "svelte-preprocess": "^4.10.6",
39
- "typescript": "^4.7.4",
39
+ "typescript": "^4.8.2",
40
40
  "uvu": "^0.5.3",
41
41
  "vite": "^3.1.0-beta.1"
42
42
  },
@@ -53,7 +53,8 @@
53
53
  "!src/core/**/fixtures",
54
54
  "!src/core/**/test",
55
55
  "types",
56
- "svelte-kit.js"
56
+ "svelte-kit.js",
57
+ "scripts/special-types"
57
58
  ],
58
59
  "exports": {
59
60
  "./package.json": "./package.json",
@@ -0,0 +1,8 @@
1
+ This module provides access to runtime environment variables, as defined by the platform you're running on. For example if you're using [`adapter-node`](https://github.com/sveltejs/kit/tree/master/packages/adapter-node) (or running [`vite preview`](https://kit.svelte.dev/docs/cli)), this is equivalent to `process.env`. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#kit-env-publicprefix).
2
+
3
+ This module cannot be imported into client-side code.
4
+
5
+ ```ts
6
+ import { env } from '$env/dynamic/private';
7
+ console.log(env.DEPLOYMENT_SPECIFIC_VARIABLE);
8
+ ```
@@ -0,0 +1,8 @@
1
+ Similar to [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), but only includes variables that begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#kit-env-publicprefix) (which defaults to `PUBLIC_`), and can therefore safely be exposed to client-side code.
2
+
3
+ Note that public dynamic environment variables must all be sent from the server to the client, causing larger network requests — when possible, use `$env/static/public` instead.
4
+
5
+ ```ts
6
+ import { env } from '$env/dynamic/public';
7
+ console.log(env.PUBLIC_DEPLOYMENT_SPECIFIC_VARIABLE);
8
+ ```
@@ -0,0 +1,19 @@
1
+ Environment variables [loaded by Vite](https://vitejs.dev/guide/env-and-mode.html#env-files) from `.env` files and `process.env`. Like [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), this module cannot be imported into client-side code. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#kit-env-publicprefix).
2
+
3
+ _Unlike_ [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), the values exported from this module are statically injected into your bundle at build time, enabling optimisations like dead code elimination.
4
+
5
+ ```ts
6
+ import { API_KEY } from '$env/static/private';
7
+ ```
8
+
9
+ Note that all environment variables referenced in your code should be declared (for example in an `.env` file), even if they don't have a value until the app is deployed:
10
+
11
+ ```
12
+ MY_FEATURE_FLAG=""
13
+ ```
14
+
15
+ You can override `.env` values from the command line like so:
16
+
17
+ ```bash
18
+ MY_FEATURE_FLAG="enabled" npm run dev
19
+ ```
@@ -0,0 +1,7 @@
1
+ Similar to [`$env/static/private`](https://kit.svelte.dev/docs/modules#$env-static-private), except that it only includes environment variables that begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#kit-env-publicprefix) (which defaults to `PUBLIC_`), and can therefore safely be exposed to client-side code.
2
+
3
+ Values are replaced statically at build time.
4
+
5
+ ```ts
6
+ import { PUBLIC_BASE_URL } from '$env/static/public';
7
+ ```
@@ -0,0 +1 @@
1
+ This is a simple alias to `src/lib`, or whatever directory is specified as [`config.kit.files.lib`](https://kit.svelte.dev/docs/configuration#files). It allows you to access common components and utility modules without `../../../../` nonsense.
@@ -5,57 +5,21 @@ import { pipeline } from 'stream';
5
5
  import { promisify } from 'util';
6
6
  import { copy, rimraf, mkdirp } from '../../utils/filesystem.js';
7
7
  import { generate_manifest } from '../generate_manifest/index.js';
8
- import { get_path } from '../../utils/routing.js';
8
+
9
+ const pipe = promisify(pipeline);
9
10
 
10
11
  /**
11
12
  * Creates the Builder which is passed to adapters for building the application.
12
13
  * @param {{
13
14
  * config: import('types').ValidatedConfig;
14
15
  * build_data: import('types').BuildData;
16
+ * routes: import('types').RouteData[];
15
17
  * prerendered: import('types').Prerendered;
16
18
  * log: import('types').Logger;
17
19
  * }} opts
18
20
  * @returns {import('types').Builder}
19
21
  */
20
- export function create_builder({ config, build_data, prerendered, log }) {
21
- /** @type {Set<string>} */
22
- const prerendered_paths = new Set(prerendered.paths);
23
-
24
- /** @param {import('types').RouteData} route */
25
- // TODO routes should come pre-filtered
26
- function not_prerendered(route) {
27
- const path = route.page && get_path(route.id);
28
- if (path) {
29
- return !prerendered_paths.has(path) && !prerendered_paths.has(path + '/');
30
- }
31
-
32
- return true;
33
- }
34
-
35
- const pipe = promisify(pipeline);
36
-
37
- /**
38
- * @param {string} file
39
- * @param {'gz' | 'br'} format
40
- */
41
- async function compress_file(file, format = 'gz') {
42
- const compress =
43
- format == 'br'
44
- ? zlib.createBrotliCompress({
45
- params: {
46
- [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
47
- [zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY,
48
- [zlib.constants.BROTLI_PARAM_SIZE_HINT]: statSync(file).size
49
- }
50
- })
51
- : zlib.createGzip({ level: zlib.constants.Z_BEST_COMPRESSION });
52
-
53
- const source = createReadStream(file);
54
- const destination = createWriteStream(`${file}.${format}`);
55
-
56
- await pipe(source, compress, destination);
57
- }
58
-
22
+ export function create_builder({ config, build_data, routes, prerendered, log }) {
59
23
  return {
60
24
  log,
61
25
  rimraf,
@@ -66,8 +30,6 @@ export function create_builder({ config, build_data, prerendered, log }) {
66
30
  prerendered,
67
31
 
68
32
  async createEntries(fn) {
69
- const { routes } = build_data.manifest_data;
70
-
71
33
  /** @type {import('types').RouteDefinition[]} */
72
34
  const facades = routes.map((route) => {
73
35
  const methods = new Set();
@@ -113,7 +75,7 @@ export function create_builder({ config, build_data, prerendered, log }) {
113
75
  }
114
76
  }
115
77
 
116
- const filtered = new Set(group.filter(not_prerendered));
78
+ const filtered = new Set(group);
117
79
 
118
80
  // heuristic: if /foo/[bar] is included, /foo/[bar].json should
119
81
  // also be included, since the page likely needs the endpoint
@@ -146,7 +108,7 @@ export function create_builder({ config, build_data, prerendered, log }) {
146
108
  return generate_manifest({
147
109
  build_data,
148
110
  relative_path: relativePath,
149
- routes: build_data.manifest_data.routes.filter(not_prerendered),
111
+ routes,
150
112
  format
151
113
  });
152
114
  },
@@ -221,3 +183,25 @@ export function create_builder({ config, build_data, prerendered, log }) {
221
183
  }
222
184
  };
223
185
  }
186
+
187
+ /**
188
+ * @param {string} file
189
+ * @param {'gz' | 'br'} format
190
+ */
191
+ async function compress_file(file, format = 'gz') {
192
+ const compress =
193
+ format == 'br'
194
+ ? zlib.createBrotliCompress({
195
+ params: {
196
+ [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
197
+ [zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY,
198
+ [zlib.constants.BROTLI_PARAM_SIZE_HINT]: statSync(file).size
199
+ }
200
+ })
201
+ : zlib.createGzip({ level: zlib.constants.Z_BEST_COMPRESSION });
202
+
203
+ const source = createReadStream(file);
204
+ const destination = createWriteStream(`${file}.${format}`);
205
+
206
+ await pipe(source, compress, destination);
207
+ }
@@ -5,14 +5,26 @@ import { create_builder } from './builder.js';
5
5
  * @param {import('types').ValidatedConfig} config
6
6
  * @param {import('types').BuildData} build_data
7
7
  * @param {import('types').Prerendered} prerendered
8
+ * @param {import('types').PrerenderMap} prerender_map
8
9
  * @param {{ log: import('types').Logger }} opts
9
10
  */
10
- export async function adapt(config, build_data, prerendered, { log }) {
11
+ export async function adapt(config, build_data, prerendered, prerender_map, { log }) {
11
12
  const { name, adapt } = config.kit.adapter;
12
13
 
13
14
  console.log(colors.bold().cyan(`\n> Using ${name}`));
14
15
 
15
- const builder = create_builder({ config, build_data, prerendered, log });
16
+ const builder = create_builder({
17
+ config,
18
+ build_data,
19
+ routes: build_data.manifest_data.routes.filter((route) => {
20
+ if (!route.page && !route.endpoint) return false;
21
+
22
+ const prerender = prerender_map.get(route.id);
23
+ return prerender === false || prerender === undefined || prerender === 'auto';
24
+ }),
25
+ prerendered,
26
+ log
27
+ });
16
28
  await adapt(builder);
17
29
 
18
30
  log.success('done');
@@ -0,0 +1,56 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>%sveltekit.message%</title>
6
+
7
+ <style>
8
+ body {
9
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
10
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: center;
14
+ height: 100vh;
15
+ }
16
+
17
+ .error {
18
+ display: flex;
19
+ align-items: center;
20
+ max-width: 32rem;
21
+ margin: 0 1rem;
22
+ }
23
+
24
+ .status {
25
+ font-weight: 200;
26
+ font-size: 3rem;
27
+ line-height: 1;
28
+ position: relative;
29
+ top: -0.05rem;
30
+ }
31
+
32
+ .message {
33
+ border-left: 1px solid #ccc;
34
+ padding: 0 0 0 1rem;
35
+ margin: 0 0 0 1rem;
36
+ min-height: 2.5rem;
37
+ display: flex;
38
+ align-items: center;
39
+ }
40
+
41
+ .message h1 {
42
+ font-weight: 400;
43
+ font-size: 1em;
44
+ margin: 0;
45
+ }
46
+ </style>
47
+ </head>
48
+ <body>
49
+ <div class="error">
50
+ <span class="status">%sveltekit.status%</span>
51
+ <div class="message">
52
+ <h1>%sveltekit.message%</h1>
53
+ </div>
54
+ </div>
55
+ </body>
56
+ </html>
@@ -10,11 +10,11 @@ import options from './options.js';
10
10
  * @param {import('types').ValidatedConfig} config
11
11
  */
12
12
  export function load_template(cwd, config) {
13
- const { template } = config.kit.files;
14
- const relative = path.relative(cwd, template);
13
+ const { appTemplate } = config.kit.files;
14
+ const relative = path.relative(cwd, appTemplate);
15
15
 
16
- if (fs.existsSync(template)) {
17
- const contents = fs.readFileSync(template, 'utf8');
16
+ if (fs.existsSync(appTemplate)) {
17
+ const contents = fs.readFileSync(appTemplate, 'utf8');
18
18
 
19
19
  // TODO remove this for 1.0
20
20
  const match = /%svelte\.([a-z]+)%/.exec(contents);
@@ -34,7 +34,17 @@ export function load_template(cwd, config) {
34
34
  throw new Error(`${relative} does not exist`);
35
35
  }
36
36
 
37
- return fs.readFileSync(template, 'utf-8');
37
+ return fs.readFileSync(appTemplate, 'utf-8');
38
+ }
39
+
40
+ /**
41
+ * Loads the error page (src/error.html by default) if it exists.
42
+ * Falls back to a generic error page content.
43
+ * @param {import('types').ValidatedConfig} config
44
+ */
45
+ export function load_error_page(config) {
46
+ const { errorTemplate } = config.kit.files;
47
+ return fs.readFileSync(errorTemplate, 'utf-8');
38
48
  }
39
49
 
40
50
  /**
@@ -64,10 +74,19 @@ function process_config(config, { cwd = process.cwd() } = {}) {
64
74
  validated.kit.outDir = path.resolve(cwd, validated.kit.outDir);
65
75
 
66
76
  for (const key in validated.kit.files) {
77
+ // TODO remove for 1.0
78
+ if (key === 'template') continue;
79
+
67
80
  // @ts-expect-error this is typescript at its stupidest
68
81
  validated.kit.files[key] = path.resolve(cwd, validated.kit.files[key]);
69
82
  }
70
83
 
84
+ if (!fs.existsSync(validated.kit.files.errorTemplate)) {
85
+ validated.kit.files.errorTemplate = url.fileURLToPath(
86
+ new URL('./default-error.html', import.meta.url)
87
+ );
88
+ }
89
+
71
90
  return validated;
72
91
  }
73
92
 
@@ -107,14 +107,16 @@ const options = object(
107
107
  return input;
108
108
  }),
109
109
 
110
- browser: validate({ hydrate: true, router: true }, (input, keypath) => {
111
- const value = object({ hydrate: boolean(true), router: boolean(true) })(input, keypath);
112
- if (!value.hydrate && value.router) {
113
- throw new Error(
114
- 'config.kit.browser.router cannot be true if config.kit.browser.hydrate is false'
115
- );
116
- }
117
- return value;
110
+ // TODO: remove this for the 1.0 release
111
+ browser: object({
112
+ hydrate: error(
113
+ (keypath) =>
114
+ `${keypath} has been removed. You can set it inside the top level +layout.js instead. See the PR for more information: https://github.com/sveltejs/kit/pull/6197`
115
+ ),
116
+ router: error(
117
+ (keypath) =>
118
+ `${keypath} has been removed. You can set it inside the top level +layout.js instead. See the PR for more information: https://github.com/sveltejs/kit/pull/6197`
119
+ )
118
120
  }),
119
121
 
120
122
  csp: object({
@@ -140,7 +142,12 @@ const options = object(
140
142
  params: string(join('src', 'params')),
141
143
  routes: string(join('src', 'routes')),
142
144
  serviceWorker: string(join('src', 'service-worker')),
143
- template: string(join('src', 'app.html'))
145
+ appTemplate: string(join('src', 'app.html')),
146
+ errorTemplate: string(join('src', 'error.html')),
147
+ // TODO: remove this for the 1.0 release
148
+ template: error(
149
+ () => 'config.kit.files.template has been renamed to config.kit.files.appTemplate'
150
+ )
144
151
  }),
145
152
 
146
153
  // TODO: remove this for the 1.0 release
@@ -221,7 +228,10 @@ const options = object(
221
228
  (keypath) =>
222
229
  `${keypath} has been removed — it is now controlled by the trailingSlash option. See https://kit.svelte.dev/docs/configuration#trailingslash`
223
230
  ),
224
- default: boolean(false),
231
+ default: error(
232
+ (keypath) =>
233
+ `${keypath} has been removed. You can set it inside the top level +layout.js instead. See the PR for more information: https://github.com/sveltejs/kit/pull/6197`
234
+ ),
225
235
  enabled: boolean(true),
226
236
  entries: validate(['*'], (input, keypath) => {
227
237
  if (!Array.isArray(input) || !input.every((page) => typeof page === 'string')) {
@@ -9,15 +9,15 @@ import { crawl } from './crawl.js';
9
9
  import { escape_html_attr } from '../../utils/escape.js';
10
10
  import { logger } from '../utils.js';
11
11
  import { load_config } from '../config/index.js';
12
- import { compact } from '../../utils/array.js';
13
- import { get_path } from '../../utils/routing.js';
12
+ import { affects_path } from '../../utils/routing.js';
13
+ import { get_option } from '../../runtime/server/utils.js';
14
14
 
15
15
  /**
16
16
  * @typedef {import('types').PrerenderErrorHandler} PrerenderErrorHandler
17
17
  * @typedef {import('types').Logger} Logger
18
18
  */
19
19
 
20
- const [, , client_out_dir, results_path, manifest_path, verbose, env] = process.argv;
20
+ const [, , client_out_dir, results_path, verbose, env] = process.argv;
21
21
 
22
22
  prerender();
23
23
 
@@ -58,12 +58,15 @@ const OK = 2;
58
58
  const REDIRECT = 3;
59
59
 
60
60
  /**
61
- * @param {import('types').Prerendered} prerendered
61
+ * @param {{
62
+ * prerendered: import('types').Prerendered;
63
+ * prerender_map: import('types').PrerenderMap;
64
+ * }} data
62
65
  */
63
- const output_and_exit = (prerendered) => {
66
+ const output_and_exit = (data) => {
64
67
  writeFileSync(
65
68
  results_path,
66
- JSON.stringify(prerendered, (_key, value) =>
69
+ JSON.stringify(data, (_key, value) =>
67
70
  value instanceof Map ? Array.from(value.entries()) : value
68
71
  )
69
72
  );
@@ -79,11 +82,14 @@ export async function prerender() {
79
82
  paths: []
80
83
  };
81
84
 
85
+ /** @type {import('types').PrerenderMap} */
86
+ const prerender_map = new Map();
87
+
82
88
  /** @type {import('types').ValidatedKitConfig} */
83
89
  const config = (await load_config()).kit;
84
90
 
85
91
  if (!config.prerender.enabled) {
86
- output_and_exit(prerendered);
92
+ output_and_exit({ prerendered, prerender_map });
87
93
  return;
88
94
  }
89
95
 
@@ -231,6 +237,16 @@ export async function prerender() {
231
237
  const encoded_dependency_path = new URL(dependency_path, 'http://localhost').pathname;
232
238
  const decoded_dependency_path = decodeURI(encoded_dependency_path);
233
239
 
240
+ const prerender = result.response.headers.get('x-sveltekit-prerender');
241
+
242
+ if (prerender) {
243
+ const route_id = /** @type {string} */ (result.response.headers.get('x-sveltekit-routeid'));
244
+ const existing_value = prerender_map.get(route_id);
245
+ if (existing_value !== 'auto') {
246
+ prerender_map.set(route_id, prerender === 'true' ? true : 'auto');
247
+ }
248
+ }
249
+
234
250
  const body = result.body ?? new Uint8Array(await result.response.arrayBuffer());
235
251
  save(
236
252
  'dependencies',
@@ -340,25 +356,54 @@ export async function prerender() {
340
356
  }
341
357
  }
342
358
 
343
- if (config.prerender.enabled) {
344
- for (const entry of config.prerender.entries) {
345
- if (entry === '*') {
346
- /** @type {import('types').SSRManifest} */
347
- const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
348
- const { routes } = manifest._;
349
- const entries = compact(routes.map((route) => route.page && get_path(route.id)));
359
+ for (const route of manifest._.routes) {
360
+ try {
361
+ if (route.endpoint) {
362
+ const mod = await route.endpoint();
363
+ if (mod.prerender !== undefined) {
364
+ if (mod.prerender && (mod.POST || mod.PATCH || mod.PUT || mod.DELETE)) {
365
+ throw new Error(
366
+ `Cannot prerender a +server file with POST, PATCH, PUT, or DELETE (${route.id})`
367
+ );
368
+ }
350
369
 
351
- for (const entry of entries) {
352
- enqueue(null, config.paths.base + entry); // TODO can we pre-normalize these?
370
+ prerender_map.set(route.id, mod.prerender);
353
371
  }
354
- } else {
355
- enqueue(null, config.paths.base + entry);
356
372
  }
373
+
374
+ if (route.page) {
375
+ const nodes = await Promise.all(
376
+ [...route.page.layouts, route.page.leaf].map((n) => {
377
+ if (n !== undefined) return manifest._.nodes[n]();
378
+ })
379
+ );
380
+ const prerender = get_option(nodes, 'prerender') ?? false;
381
+
382
+ prerender_map.set(route.id, prerender);
383
+ }
384
+ } catch (e) {
385
+ // We failed to import the module, which indicates it can't be prerendered
386
+ // TODO should we catch these? It's almost certainly a bug in the app
387
+ console.error(e);
357
388
  }
389
+ }
358
390
 
359
- await q.done();
391
+ for (const entry of config.prerender.entries) {
392
+ if (entry === '*') {
393
+ for (const [id, prerender] of prerender_map) {
394
+ if (prerender) {
395
+ if (id.includes('[')) continue;
396
+ const path = `/${id.split('/').filter(affects_path).join('/')}`;
397
+ enqueue(null, config.paths.base + path);
398
+ }
399
+ }
400
+ } else {
401
+ enqueue(null, config.paths.base + entry);
402
+ }
360
403
  }
361
404
 
405
+ await q.done();
406
+
362
407
  const rendered = await server.respond(new Request(config.prerender.origin + '/[fallback]'), {
363
408
  getClientAddress,
364
409
  prerendering: {
@@ -371,7 +416,7 @@ export async function prerender() {
371
416
  mkdirp(dirname(file));
372
417
  writeFileSync(file, await rendered.text());
373
418
 
374
- output_and_exit(prerendered);
419
+ output_and_exit({ prerendered, prerender_map });
375
420
  }
376
421
 
377
422
  /** @return {string} */
@@ -181,7 +181,7 @@ function update_types(config, routes, route) {
181
181
  `type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>`
182
182
  );
183
183
  // null & {} == null, we need to prevent that in some situations
184
- declarations.push(`type EnsureParentData<T> = NonNullable<T> extends never ? {} : T;`);
184
+ declarations.push(`type EnsureParentData<T> = T extends null | undefined ? {} : T;`);
185
185
  }
186
186
 
187
187
  if (route.leaf) {
@@ -7,19 +7,37 @@ export function sequence(...handlers) {
7
7
  if (!length) return ({ event, resolve }) => resolve(event);
8
8
 
9
9
  return ({ event, resolve }) => {
10
- return apply_handle(0, event);
10
+ return apply_handle(0, event, {});
11
11
 
12
12
  /**
13
13
  * @param {number} i
14
14
  * @param {import('types').RequestEvent} event
15
+ * @param {import('types').ResolveOptions | undefined} parent_options
15
16
  * @returns {import('types').MaybePromise<Response>}
16
17
  */
17
- function apply_handle(i, event) {
18
+ function apply_handle(i, event, parent_options) {
18
19
  const handle = handlers[i];
19
20
 
20
21
  return handle({
21
22
  event,
22
- resolve: i < length - 1 ? (event) => apply_handle(i + 1, event) : resolve
23
+ resolve: (event, options) => {
24
+ /** @param {{ html: string, done: boolean }} opts */
25
+ const transformPageChunk = async ({ html, done }) => {
26
+ if (options?.transformPageChunk) {
27
+ html = (await options.transformPageChunk({ html, done })) ?? '';
28
+ }
29
+
30
+ if (parent_options?.transformPageChunk) {
31
+ html = (await parent_options.transformPageChunk({ html, done })) ?? '';
32
+ }
33
+
34
+ return html;
35
+ };
36
+
37
+ return i < length - 1
38
+ ? apply_handle(i + 1, event, { transformPageChunk })
39
+ : resolve(event, { transformPageChunk });
40
+ }
23
41
  });
24
42
  }
25
43
  };
@@ -2,7 +2,7 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { mkdirp, posixify } from '../../../utils/filesystem.js';
4
4
  import { get_vite_config, merge_vite_configs, resolve_entry } from '../utils.js';
5
- import { load_template } from '../../../core/config/index.js';
5
+ import { load_error_page, load_template } from '../../../core/config/index.js';
6
6
  import { runtime_directory } from '../../../core/utils.js';
7
7
  import { create_build, find_deps, get_default_build_config, is_http_method } from './utils.js';
8
8
  import { s } from '../../../utils/misc.js';
@@ -14,9 +14,10 @@ import { s } from '../../../utils/misc.js';
14
14
  * has_service_worker: boolean;
15
15
  * runtime: string;
16
16
  * template: string;
17
+ * error_page: string;
17
18
  * }} opts
18
19
  */
19
- const server_template = ({ config, hooks, has_service_worker, runtime, template }) => `
20
+ const server_template = ({ config, hooks, has_service_worker, runtime, template, error_page }) => `
20
21
  import root from '__GENERATED__/root.svelte';
21
22
  import { respond } from '${runtime}/server/index.js';
22
23
  import { set_paths, assets, base } from '${runtime}/paths.js';
@@ -24,12 +25,16 @@ import { set_prerendering } from '${runtime}/env.js';
24
25
  import { set_private_env } from '${runtime}/env-private.js';
25
26
  import { set_public_env } from '${runtime}/env-public.js';
26
27
 
27
- const template = ({ head, body, assets, nonce }) => ${s(template)
28
+ const app_template = ({ head, body, assets, nonce }) => ${s(template)
28
29
  .replace('%sveltekit.head%', '" + head + "')
29
30
  .replace('%sveltekit.body%', '" + body + "')
30
31
  .replace(/%sveltekit\.assets%/g, '" + assets + "')
31
32
  .replace(/%sveltekit\.nonce%/g, '" + nonce + "')};
32
33
 
34
+ const error_template = ({ status, message }) => ${s(error_page)
35
+ .replace(/%sveltekit\.status%/g, '" + status + "')
36
+ .replace(/%sveltekit\.message%/g, '" + message + "')};
37
+
33
38
  let read = null;
34
39
 
35
40
  set_paths(${s(config.kit.paths)});
@@ -65,21 +70,16 @@ export class Server {
65
70
  error.stack = this.options.get_stack(error);
66
71
  },
67
72
  hooks: null,
68
- hydrate: ${s(config.kit.browser.hydrate)},
69
73
  manifest,
70
74
  method_override: ${s(config.kit.methodOverride)},
71
75
  paths: { base, assets },
72
- prerender: {
73
- default: ${config.kit.prerender.default},
74
- enabled: ${config.kit.prerender.enabled}
75
- },
76
76
  public_env: {},
77
77
  read,
78
78
  root,
79
79
  service_worker: ${has_service_worker ? "base + '/service-worker.js'" : 'null'},
80
- router: ${s(config.kit.browser.router)},
81
- template,
82
- template_contains_nonce: ${template.includes('%sveltekit.nonce%')},
80
+ app_template,
81
+ app_template_contains_nonce: ${template.includes('%sveltekit.nonce%')},
82
+ error_template,
83
83
  trailing_slash: ${s(config.kit.trailingSlash)}
84
84
  };
85
85
  }
@@ -205,7 +205,8 @@ export async function build_server(options, client) {
205
205
  hooks: app_relative(hooks_file),
206
206
  has_service_worker: config.kit.serviceWorker.register && !!service_worker_entry_file,
207
207
  runtime: posixify(path.relative(build_dir, runtime_directory)),
208
- template: load_template(cwd, config)
208
+ template: load_template(cwd, config),
209
+ error_page: load_error_page(config)
209
210
  })
210
211
  );
211
212