@sveltejs/kit 3.0.0-next.10 → 3.0.0-next.12

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.
Files changed (88) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +8 -8
  3. package/src/constants.js +3 -1
  4. package/src/core/adapt/builder.js +3 -6
  5. package/src/core/config/index.js +6 -1
  6. package/src/core/config/options.js +54 -104
  7. package/src/core/env.js +25 -11
  8. package/src/core/postbuild/fallback.js +2 -1
  9. package/src/core/postbuild/prerender.js +75 -28
  10. package/src/core/postbuild/queue.js +2 -1
  11. package/src/core/sync/sync.js +11 -14
  12. package/src/core/sync/utils.js +21 -1
  13. package/src/core/sync/{write_non_ambient.js → write_app_types.js} +23 -21
  14. package/src/core/sync/write_client_manifest.js +4 -12
  15. package/src/core/sync/write_env.js +6 -4
  16. package/src/core/sync/write_server.js +12 -17
  17. package/src/core/sync/write_tsconfig/index.js +245 -0
  18. package/src/core/sync/write_tsconfig/utils.js +162 -0
  19. package/src/core/sync/write_types/index.js +80 -88
  20. package/src/core/utils.js +28 -3
  21. package/src/exports/env/index.js +68 -3
  22. package/src/exports/internal/env.js +6 -3
  23. package/src/exports/internal/server/index.js +3 -1
  24. package/src/exports/params.js +9 -4
  25. package/src/exports/public.d.ts +69 -71
  26. package/src/exports/vite/dev/index.js +112 -56
  27. package/src/exports/vite/index.js +483 -258
  28. package/src/exports/vite/module_ids.js +2 -2
  29. package/src/exports/vite/preview/index.js +3 -2
  30. package/src/exports/vite/utils.js +21 -10
  31. package/src/runner.js +13 -0
  32. package/src/runtime/app/forms.js +4 -0
  33. package/src/runtime/app/manifest/index.js +1 -0
  34. package/src/runtime/app/paths/client.js +30 -30
  35. package/src/runtime/app/paths/internal/client.js +26 -0
  36. package/src/runtime/app/paths/server.js +23 -10
  37. package/src/runtime/app/paths/types.d.ts +11 -19
  38. package/src/runtime/app/server/index.js +1 -1
  39. package/src/runtime/app/server/remote/command.js +12 -7
  40. package/src/runtime/app/server/remote/form.js +29 -26
  41. package/src/runtime/app/server/remote/prerender.js +9 -2
  42. package/src/runtime/app/server/remote/query.js +6 -5
  43. package/src/runtime/app/server/remote/shared.js +48 -30
  44. package/src/runtime/app/service-worker/index.js +24 -0
  45. package/src/runtime/app/state/index.js +1 -1
  46. package/src/runtime/client/client.js +46 -9
  47. package/src/runtime/client/ndjson.js +1 -1
  48. package/src/runtime/client/remote-functions/form.svelte.js +43 -51
  49. package/src/runtime/client/remote-functions/query/index.js +2 -2
  50. package/src/runtime/client/remote-functions/query-batch.svelte.js +2 -3
  51. package/src/runtime/client/remote-functions/query-live/iterator.js +5 -2
  52. package/src/runtime/client/remote-functions/shared.svelte.js +4 -1
  53. package/src/runtime/client/state.svelte.js +54 -21
  54. package/src/runtime/client/stream.js +3 -2
  55. package/src/runtime/form-utils.js +89 -54
  56. package/src/runtime/server/cookie.js +1 -1
  57. package/src/runtime/server/data/index.js +32 -29
  58. package/src/runtime/server/errors.js +135 -0
  59. package/src/runtime/server/fetch.js +1 -1
  60. package/src/runtime/server/index.js +20 -10
  61. package/src/runtime/server/internal.js +25 -0
  62. package/src/runtime/server/page/actions.js +4 -3
  63. package/src/runtime/server/page/data_serializer.js +10 -10
  64. package/src/runtime/server/page/index.js +3 -2
  65. package/src/runtime/server/page/load_data.js +1 -1
  66. package/src/runtime/server/page/render.js +22 -35
  67. package/src/runtime/server/page/respond_with_error.js +6 -5
  68. package/src/runtime/server/{remote.js → remote-functions.js} +67 -36
  69. package/src/runtime/server/respond.js +58 -20
  70. package/src/runtime/server/sourcemaps.js +183 -0
  71. package/src/runtime/server/utils.js +12 -157
  72. package/src/types/ambient-private.d.ts +10 -0
  73. package/src/types/ambient.d.ts +22 -27
  74. package/src/types/global-private.d.ts +12 -5
  75. package/src/types/internal.d.ts +19 -9
  76. package/src/types/private.d.ts +8 -0
  77. package/src/utils/escape.js +9 -25
  78. package/src/utils/features.js +1 -1
  79. package/src/utils/fork.js +7 -2
  80. package/src/utils/page_nodes.js +3 -5
  81. package/src/utils/url.js +12 -0
  82. package/src/version.js +1 -1
  83. package/types/index.d.ts +152 -120
  84. package/types/index.d.ts.map +6 -1
  85. package/src/core/sync/write_ambient.js +0 -18
  86. package/src/core/sync/write_tsconfig.js +0 -258
  87. package/src/runtime/shared-server.js +0 -7
  88. /package/src/core/sync/{write_tsconfig_test → write_tsconfig/test-app}/package.json +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "3.0.0-next.10",
3
+ "version": "3.0.0-next.12",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
package/src/cli.js CHANGED
@@ -75,19 +75,19 @@ if (!command) {
75
75
  }
76
76
 
77
77
  if (command === 'sync') {
78
- // create placeholder .svelte-kit/tsconfig.json if necessary, to squelch warnings.
78
+ // create placeholder node_modules/$app/tsconfig/tsconfig.json if necessary, to squelch warnings.
79
79
  // this isn't bulletproof — if someone has some esoteric config, it will continue
80
80
  // to harmlessly warn — but we handle the 90% case and clean up after ourselves
81
- const sveltekit_dir = '.svelte-kit';
82
- const base_tsconfig = `${sveltekit_dir}/tsconfig.json`;
81
+ const dir = 'node_modules/$app/tsconfig';
82
+ const base_tsconfig = `${dir}/tsconfig.json`;
83
83
  const base_tsconfig_json = '{}';
84
84
 
85
- const sveltekit_dir_exists = fs.existsSync(sveltekit_dir);
85
+ const sveltekit_dir_exists = fs.existsSync(dir);
86
86
  const base_tsconfig_exists = fs.existsSync(base_tsconfig);
87
87
 
88
88
  if (!base_tsconfig_exists) {
89
89
  try {
90
- fs.mkdirSync('.svelte-kit');
90
+ fs.mkdirSync(dir, { recursive: true });
91
91
  } catch {
92
92
  // ignore
93
93
  }
@@ -100,7 +100,7 @@ if (command === 'sync') {
100
100
  const sveltekit_config = extract_svelte_config(vite_config);
101
101
 
102
102
  const sync = await import('./core/sync/sync.js');
103
- sync.all_types(sveltekit_config);
103
+ sync.all_types(sveltekit_config, vite_config.root);
104
104
 
105
105
  const explicit_env_entry = resolve_explicit_env_entry(sveltekit_config.kit);
106
106
  await sync.env(sveltekit_config.kit, explicit_env_entry, vite_config.root, values.mode);
@@ -113,8 +113,8 @@ if (command === 'sync') {
113
113
  fs.unlinkSync(base_tsconfig);
114
114
  }
115
115
 
116
- if (!sveltekit_dir_exists && fs.readdirSync(sveltekit_dir).length === 0) {
117
- fs.rmSync(sveltekit_dir, { recursive: true });
116
+ if (!sveltekit_dir_exists && fs.readdirSync(dir).length === 0) {
117
+ fs.rmSync(dir, { recursive: true });
118
118
  }
119
119
  }
120
120
  } else {
package/src/constants.js CHANGED
@@ -4,10 +4,12 @@
4
4
  */
5
5
  export const SVELTE_KIT_ASSETS = '/_svelte_kit_assets';
6
6
 
7
- export const GENERATED_COMMENT = '// this file is generated — do not edit it\n';
7
+ export const GENERATED_COMMENT = '// this file is generated — do not edit it';
8
8
 
9
9
  export const ENDPOINT_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'];
10
10
 
11
11
  export const MUTATIVE_METHODS = ['POST', 'PUT', 'PATCH', 'DELETE'];
12
12
 
13
13
  export const PAGE_METHODS = ['GET', 'POST', 'HEAD'];
14
+
15
+ export const SRC_ROOT = import.meta.dirname;
@@ -8,7 +8,7 @@ import * as devalue from 'devalue';
8
8
  import { createReadStream, createWriteStream, existsSync, statSync } from 'node:fs';
9
9
  import { extname, resolve, join, dirname, relative } from 'node:path';
10
10
  import { pipeline } from 'node:stream';
11
- import { promisify, styleText } from 'node:util';
11
+ import { promisify } from 'node:util';
12
12
  import zlib from 'node:zlib';
13
13
  import { copy, rimraf, mkdirp } from '../../utils/filesystem.js';
14
14
  import { posixify } from '../../utils/os.js';
@@ -142,11 +142,8 @@ export function create_builder({
142
142
  });
143
143
 
144
144
  if (existsSync(dest)) {
145
- console.log(
146
- styleText(
147
- ['bold', 'yellow'],
148
- `Overwriting ${dest} with fallback page. Consider using a different name for the fallback.`
149
- )
145
+ log.warn(
146
+ `\nOverwriting ${dest} with fallback page. Consider using a different name for the fallback.\n`
150
147
  );
151
148
  }
152
149
 
@@ -6,6 +6,7 @@ import fs from 'node:fs';
6
6
  import path from 'node:path';
7
7
  import process from 'node:process';
8
8
  import * as url from 'node:url';
9
+ import { styleText } from 'node:util';
9
10
  import {
10
11
  validate_kit_options,
11
12
  kit_options,
@@ -223,6 +224,10 @@ export function validate_config(config) {
223
224
  return validated;
224
225
  } catch (e) {
225
226
  const error = /** @type {Error} */ (e);
226
- throw stackless(`Error loading SvelteKit options from Vite config: ${error.message}`);
227
+
228
+ // Print a nicer version of the error to the console
229
+ console.log(styleText(['bold', 'red'], `\n${error.message}\n`));
230
+
231
+ throw stackless('Failed to load SvelteKit options from Vite config');
227
232
  }
228
233
  }
@@ -1,6 +1,7 @@
1
1
  /** @import { SvelteConfig } from '@sveltejs/vite-plugin-svelte' */
2
2
  /** @import { ValidatedKitConfig } from 'types' */
3
3
  /** @import { Validator } from './types.js' */
4
+ import { styleText } from 'node:util';
4
5
 
5
6
  const directives = object({
6
7
  'child-src': string_array(),
@@ -60,6 +61,12 @@ export const validate_svelte_options = object(
60
61
  true
61
62
  );
62
63
 
64
+ const prerender_handler = validate(undefined, (input, keypath) => {
65
+ if (typeof input === 'function') return input;
66
+ if (['fail', 'warn', 'ignore'].includes(input)) return input;
67
+ throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
68
+ });
69
+
63
70
  /** @type {Validator<ValidatedKitConfig>} */
64
71
  export const validate_kit_options = object({
65
72
  adapter: validate(undefined, (input, keypath) => {
@@ -71,17 +78,21 @@ export const validate_kit_options = object({
71
78
  return input;
72
79
  }),
73
80
 
74
- alias: validate({}, (input, keypath) => {
75
- if (typeof input !== 'object') {
76
- throw new Error(`${keypath} should be an object`);
77
- }
81
+ alias: deprecate(
82
+ validate({}, (input, keypath) => {
83
+ if (typeof input !== 'object') {
84
+ throw new Error(`${keypath} should be an object`);
85
+ }
78
86
 
79
- for (const key in input) {
80
- assert_string(input[key], `${keypath}.${key}`);
81
- }
87
+ for (const key in input) {
88
+ assert_string(input[key], `${keypath}.${key}`);
89
+ }
82
90
 
83
- return input;
84
- }),
91
+ return input;
92
+ }),
93
+ (keypath) =>
94
+ `The \`${keypath}\` option is deprecated, and will be removed in a future version of SvelteKit. Use subpath imports instead: https://svelte.dev/docs/kit/$lib`
95
+ ),
85
96
 
86
97
  appDir: validate('_app', (input, keypath) => {
87
98
  assert_string(input, keypath);
@@ -247,75 +258,11 @@ export const validate_kit_options = object({
247
258
  return input;
248
259
  }),
249
260
 
250
- handleHttpError: validate(
251
- (/** @type {any} */ { message }) => {
252
- throw new Error(
253
- message +
254
- '\nTo suppress or handle this error, implement `handleHttpError` in https://svelte.dev/docs/kit/configuration#prerender'
255
- );
256
- },
257
- (input, keypath) => {
258
- if (typeof input === 'function') return input;
259
- if (['fail', 'warn', 'ignore'].includes(input)) return input;
260
- throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
261
- }
262
- ),
263
-
264
- handleMissingId: validate(
265
- (/** @type {any} */ { message }) => {
266
- throw new Error(
267
- message +
268
- '\nTo suppress or handle this error, implement `handleMissingId` in https://svelte.dev/docs/kit/configuration#prerender'
269
- );
270
- },
271
- (input, keypath) => {
272
- if (typeof input === 'function') return input;
273
- if (['fail', 'warn', 'ignore'].includes(input)) return input;
274
- throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
275
- }
276
- ),
277
-
278
- handleEntryGeneratorMismatch: validate(
279
- (/** @type {any} */ { message }) => {
280
- throw new Error(
281
- message +
282
- '\nTo suppress or handle this error, implement `handleEntryGeneratorMismatch` in https://svelte.dev/docs/kit/configuration#prerender'
283
- );
284
- },
285
- (input, keypath) => {
286
- if (typeof input === 'function') return input;
287
- if (['fail', 'warn', 'ignore'].includes(input)) return input;
288
- throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
289
- }
290
- ),
291
-
292
- handleUnseenRoutes: validate(
293
- (/** @type {any} */ { message }) => {
294
- throw new Error(
295
- message +
296
- '\nTo suppress or handle this error, implement `handleUnseenRoutes` in https://svelte.dev/docs/kit/configuration#prerender'
297
- );
298
- },
299
- (input, keypath) => {
300
- if (typeof input === 'function') return input;
301
- if (['fail', 'warn', 'ignore'].includes(input)) return input;
302
- throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
303
- }
304
- ),
305
-
306
- handleInvalidUrl: validate(
307
- (/** @type {any} */ { message }) => {
308
- throw new Error(
309
- message +
310
- '\nTo suppress or handle this error, implement `handleInvalidUrl` in https://svelte.dev/docs/kit/configuration#prerender'
311
- );
312
- },
313
- (input, keypath) => {
314
- if (typeof input === 'function') return input;
315
- if (['fail', 'warn', 'ignore'].includes(input)) return input;
316
- throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
317
- }
318
- ),
261
+ handleHttpError: prerender_handler,
262
+ handleMissingId: prerender_handler,
263
+ handleEntryGeneratorMismatch: prerender_handler,
264
+ handleUnseenRoutes: prerender_handler,
265
+ handleInvalidUrl: prerender_handler,
319
266
 
320
267
  origin: removed(
321
268
  (keypath) => `\`${keypath}\` has been removed in favour of \`config.paths.origin\``
@@ -328,47 +275,50 @@ export const validate_kit_options = object({
328
275
  }),
329
276
 
330
277
  serviceWorker: object({
278
+ files: removed(),
331
279
  register: boolean(true),
332
280
  // options could be undefined but if it is defined we only validate that
333
281
  // it's an object since the type comes from the browser itself
334
- options: validate(undefined, object({}, true)),
335
- files: fun((filename) => !/\.DS_Store/.test(filename))
282
+ options: validate(undefined, object({}, true))
336
283
  }),
337
284
 
338
285
  tracing: object({
339
286
  server: boolean(false)
340
287
  }),
341
288
 
342
- typescript: object({
343
- config: fun((config) => config)
344
- }),
289
+ typescript: deprecate(
290
+ object({
291
+ config: fun((config) => config)
292
+ }),
293
+ (keypath) => {
294
+ return `The \`${keypath}\` option is deprecated, and will be removed in a future version. Add configuration to tsconfig.json directly`;
295
+ }
296
+ ),
345
297
 
346
298
  version: object({
347
299
  name: string(Date.now().toString()),
348
- pollInterval: number(0)
300
+ pollInterval: number(3_600_000)
349
301
  })
350
302
  });
351
303
 
352
- // /**
353
- // * @param {Validator} fn
354
- // * @param {(keypath: string) => string} get_message
355
- // * @returns {Validator}
356
- // */
357
- // function deprecate(
358
- // fn,
359
- // get_message = (keypath) =>
360
- // `The \`${keypath}\` option is deprecated, and will be removed in a future version`
361
- // ) {
362
- // return (input, keypath) => {
363
- // keypath = remove_kit_prefix(keypath);
364
- //
365
- // if (input !== undefined) {
366
- // console.warn(styleText(['bold', 'yellow'], get_message(keypath)));
367
- // }
368
-
369
- // return fn(input, keypath);
370
- // };
371
- // }
304
+ /**
305
+ * @param {Validator} fn
306
+ * @param {(keypath: string) => string} get_message
307
+ * @returns {Validator}
308
+ */
309
+ function deprecate(
310
+ fn,
311
+ get_message = (keypath) =>
312
+ `The \`${keypath}\` option is deprecated, and will be removed in a future version`
313
+ ) {
314
+ return (input, keypath) => {
315
+ if (input !== undefined) {
316
+ console.warn(styleText(['bold', 'yellow'], get_message(keypath)));
317
+ }
318
+
319
+ return fn(input, keypath);
320
+ };
321
+ }
372
322
 
373
323
  // Derive the names of SvelteKit's own config options from the schema, so they
374
324
  // stay in sync automatically. These are used to separate Kit's options from
package/src/core/env.js CHANGED
@@ -10,6 +10,7 @@ import { runtime_directory } from './utils.js';
10
10
  import { resolve_entry } from '../utils/filesystem.js';
11
11
  import { handle_issues, validate } from '../exports/internal/env.js';
12
12
  import { get_config_aliases } from '../exports/vite/utils.js';
13
+ import { get_runner } from '../runner.js';
13
14
 
14
15
  /**
15
16
  * @typedef {'public' | 'private'} EnvType
@@ -52,12 +53,14 @@ export async function load_explicit_env(kit, file, root, mode) {
52
53
  /** @type {Record<string, EnvVarConfig<any>>} */
53
54
  let variables;
54
55
 
56
+ const runner = get_runner(server);
57
+
55
58
  /** @type {import('../runtime/app/env/internal.js')} */ (
56
- await server.ssrLoadModule(`${runtime_directory}/app/env/internal.js`)
59
+ await runner.import(`${runtime_directory}/app/env/internal.js`)
57
60
  ).set_building();
58
61
 
59
62
  try {
60
- ({ variables } = await server.ssrLoadModule(file));
63
+ ({ variables } = await runner.import(file));
61
64
 
62
65
  if (!variables || typeof variables !== 'object') {
63
66
  throw new Error(`${file} must export a variables object`);
@@ -238,25 +241,35 @@ export function create_sveltekit_env_public(variables, env, prelude) {
238
241
  * `env.js`. If there are none, values are inlined.
239
242
  * @param {Record<string, EnvVarConfig<any>> | null} variables
240
243
  * @param {Record<string, string>} env
244
+ * @param {string} version
241
245
  * @param {string} global
242
246
  * @param {string} base
243
247
  * @param {string} app_dir
244
248
  */
245
- export function create_sveltekit_env_service_worker(variables, env, global, base, app_dir) {
249
+ export function create_sveltekit_env_service_worker(
250
+ variables,
251
+ env,
252
+ version,
253
+ global,
254
+ base,
255
+ app_dir
256
+ ) {
246
257
  const has_dynamic_public_env = Object.values(variables ?? {}).some(
247
258
  (config) => config.public && !config.static
248
259
  );
249
260
 
250
261
  if (!has_dynamic_public_env) {
251
- return create_sveltekit_env_service_worker_dev(variables, env, global);
262
+ return create_sveltekit_env_service_worker_dev(variables, env, version, global);
252
263
  }
253
264
 
254
265
  return dedent`
255
266
  import { env } from '${base}/${app_dir}/env.js';
256
267
 
257
- globalThis.__SVELTEKIT_EXPERIMENTAL_EXPLICIT_ENVIRONMENT_VARIABLES__ = true;
258
-
259
- ${global} = { env };
268
+ ${global} = {
269
+ base: location.pathname.split('/').slice(0, -1).join('/'),
270
+ env,
271
+ version: ${JSON.stringify(version)}
272
+ };
260
273
  `;
261
274
  }
262
275
 
@@ -264,9 +277,10 @@ export function create_sveltekit_env_service_worker(variables, env, global, base
264
277
  * Creates the `__sveltekit/env/service-worker` module used in development
265
278
  * @param {Record<string, EnvVarConfig<any>> | null} variables
266
279
  * @param {Record<string, string>} env
280
+ * @param {string} version
267
281
  * @param {string} global
268
282
  */
269
- export function create_sveltekit_env_service_worker_dev(variables, env, global) {
283
+ export function create_sveltekit_env_service_worker_dev(variables, env, version, global) {
270
284
  /** @type {string[]} */
271
285
  const properties = [];
272
286
 
@@ -283,12 +297,12 @@ export function create_sveltekit_env_service_worker_dev(variables, env, global)
283
297
  handle_issues(issues);
284
298
 
285
299
  return dedent`
286
- globalThis.__SVELTEKIT_EXPERIMENTAL_EXPLICIT_ENVIRONMENT_VARIABLES__ = true;
287
-
288
300
  ${global} = {
301
+ base: location.pathname.split('/').slice(0, -1).join('/'),
289
302
  env: {
290
303
  ${properties.join(',\n\t\t') || '// empty'}
291
- }
304
+ },
305
+ version: ${JSON.stringify(version)}
292
306
  };
293
307
  `;
294
308
  }
@@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  import { forked } from '../../utils/fork.js';
5
+ import { stackless } from '../../utils/error.js';
5
6
 
6
7
  export default forked(import.meta.url, generate_fallback);
7
8
 
@@ -47,5 +48,5 @@ async function generate_fallback({ manifest_path, env, out_dir, origin, assets }
47
48
  return await response.text();
48
49
  }
49
50
 
50
- throw new Error(`Could not create a fallback page — failed with status ${response.status}`);
51
+ throw stackless('Could not create a fallback page');
51
52
  }
@@ -16,6 +16,7 @@ import * as devalue from 'devalue';
16
16
  import { createReadableStream } from '@sveltejs/kit/node';
17
17
  import generate_fallback from './fallback.js';
18
18
  import { stringify_remote_arg } from '../../runtime/shared.js';
19
+ import { log_response } from '../../exports/vite/utils.js';
19
20
 
20
21
  export default forked(import.meta.url, prerender);
21
22
 
@@ -57,29 +58,60 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
57
58
  /** @type {import('types').ServerModule} */
58
59
  const { Server } = await import(pathToFileURL(`${out}/server/index.js`).href);
59
60
 
61
+ const throw_handled = () => {
62
+ throw new Error('__handled__');
63
+ };
64
+
60
65
  /**
61
66
  * @template {{message: string}} T
62
67
  * @template {Omit<T, 'message'>} K
63
- * @param {import('types').Logger} log
64
- * @param {'fail' | 'warn' | 'ignore' | ((details: T) => void)} input
68
+ * @param {string} name
69
+ * @param {'fail' | 'warn' | 'ignore' | undefined | ((details: T) => void)} input
65
70
  * @param {(details: K) => string} format
66
- * @returns {(details: K) => void}
71
+ * @returns {(details: K & { error?: unknown }) => void}
67
72
  */
68
- function normalise_error_handler(log, input, format) {
73
+ function normalise_error_handler(name, input, format) {
74
+ /**
75
+ * @param {any} details
76
+ */
77
+ function log_failure(details) {
78
+ const message = format(details);
79
+ log.error(`\n${message}\n`);
80
+ }
81
+
69
82
  switch (input) {
70
83
  case 'fail':
71
84
  return (details) => {
72
- throw new Error(format(details));
85
+ log_failure(details);
86
+ throw_handled();
73
87
  };
74
88
  case 'warn':
75
- return (details) => {
76
- log.error(format(details));
77
- };
89
+ return log_failure;
78
90
  case 'ignore':
79
91
  return noop;
92
+ case undefined: {
93
+ return (details) => {
94
+ log_failure(details);
95
+
96
+ log.err(
97
+ `To suppress or handle this error, implement \`${name}\` in https://svelte.dev/docs/kit/configuration#prerender\n`
98
+ );
99
+
100
+ throw_handled();
101
+ };
102
+ }
80
103
  default:
81
- // @ts-expect-error TS thinks T might be of a different kind, but it's not
82
- return (details) => input({ ...details, message: format(details) });
104
+ return (details) => {
105
+ const message = format(details);
106
+
107
+ try {
108
+ // @ts-expect-error TS thinks T might be of a different kind, but it's not
109
+ input({ ...details, message });
110
+ } catch (error) {
111
+ log.prettyError(error, import.meta.dirname);
112
+ throw_handled();
113
+ }
114
+ };
83
115
  }
84
116
  }
85
117
 
@@ -141,48 +173,55 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
141
173
  const saved = new Map();
142
174
 
143
175
  const handle_http_error = normalise_error_handler(
144
- log,
176
+ 'handleHttpError',
145
177
  config.prerender.handleHttpError,
146
178
  ({ status, path, referrer, referenceType }) => {
147
- const message =
148
- status === 404 && !path.startsWith(config.paths.base)
149
- ? `${path} does not begin with \`base\`. You can fix this by using \`resolve('${path}')\` from \`$app/paths\`. The base path is configurable from \`paths.base\` - see https://svelte.dev/docs/kit/configuration#paths for more info`
150
- : path;
179
+ let message = `Failed to prerender ${path}`;
180
+
181
+ if (status === 404) {
182
+ if (!path.startsWith(config.paths.base)) {
183
+ message = referrer ? `${path} (${referenceType} from ${referrer})` : path;
184
+
185
+ message += ` does not begin with \`base\`. You can fix this by using \`resolve('${path}')\` from \`$app/paths\`. The base path is configurable from \`paths.base\``;
186
+ } else if (referrer) {
187
+ message = `${path} was ${referenceType} from ${referrer}`;
188
+ }
189
+ }
151
190
 
152
- return `${status} ${message}${referrer ? ` (${referenceType} from ${referrer})` : ''}`;
191
+ return message;
153
192
  }
154
193
  );
155
194
 
156
195
  const handle_missing_id = normalise_error_handler(
157
- log,
196
+ 'handleMissingId',
158
197
  config.prerender.handleMissingId,
159
198
  ({ path, id, referrers }) => {
160
199
  return (
161
- `The following pages contain links to ${path}#${id}, but no element with id="${id}" exists on ${path} - see the \`handleMissingId\` option in https://svelte.dev/docs/kit/configuration#prerender for more info:` +
200
+ `The following pages contain links to ${path}#${id}, but no element with id="${id}" exists on ${path}:` +
162
201
  referrers.map((l) => `\n - ${l}`).join('')
163
202
  );
164
203
  }
165
204
  );
166
205
 
167
206
  const handle_entry_generator_mismatch = normalise_error_handler(
168
- log,
207
+ 'handleEntryGeneratorMismatch',
169
208
  config.prerender.handleEntryGeneratorMismatch,
170
209
  ({ generatedFromId, entry, matchedId }) => {
171
- return `The entries export from ${generatedFromId} generated entry ${entry}, which was matched by ${matchedId} - see the \`handleEntryGeneratorMismatch\` option in https://svelte.dev/docs/kit/configuration#prerender for more info.`;
210
+ return `The entries export from ${generatedFromId} generated entry ${entry}, which was matched by ${matchedId === entry ? 'a static route' : matchedId}`;
172
211
  }
173
212
  );
174
213
 
175
214
  const handle_not_prerendered_route = normalise_error_handler(
176
- log,
215
+ 'handleUnseenRoutes',
177
216
  config.prerender.handleUnseenRoutes,
178
217
  ({ routes }) => {
179
218
  const list = routes.map((id) => ` - ${id}`).join('\n');
180
- return `The following routes were marked as prerenderable, but were not prerendered because they were not found while crawling your app:\n${list}\n\nSee the \`handleUnseenRoutes\` option in https://svelte.dev/docs/kit/configuration#prerender for more info.`;
219
+ return `The following routes were marked as prerenderable, but were not prerendered because they were not found while crawling your app:\n${list}`;
181
220
  }
182
221
  );
183
222
 
184
223
  const handle_invalid_url = normalise_error_handler(
185
- log,
224
+ 'handleInvalidUrl',
186
225
  config.prerender.handleInvalidUrl,
187
226
  ({ href, referrer }) => {
188
227
  return `Invalid URL ${href}${referrer ? ` (linked from ${referrer})` : ''}`;
@@ -260,7 +299,9 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
260
299
  /** @type {Map<string, import('types').PrerenderDependency>} */
261
300
  const dependencies = new Map();
262
301
 
263
- const response = await server.respond(new Request(prerender_origin + encoded), {
302
+ const request = new Request(prerender_origin + encoded);
303
+
304
+ const response = await server.respond(request, {
264
305
  getClientAddress() {
265
306
  throw new Error('Cannot read clientAddress during prerendering');
266
307
  },
@@ -298,6 +339,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
298
339
  });
299
340
  }
300
341
 
342
+ if (response.status !== 204) {
343
+ log_response(response.status, request);
344
+ }
345
+
301
346
  const body = Buffer.from(await response.arrayBuffer());
302
347
 
303
348
  const category = decoded.startsWith(remote_prefix) ? 'data' : 'pages';
@@ -428,8 +473,6 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
428
473
  if (!headers['x-sveltekit-normalize']) {
429
474
  mkdirp(dirname(dest));
430
475
 
431
- log.warn(`${response.status} ${decoded} -> ${location}`);
432
-
433
476
  writeFileSync(
434
477
  dest,
435
478
  `<script>location.href=${devalue.uneval(
@@ -476,7 +519,6 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
476
519
 
477
520
  mkdirp(dir);
478
521
 
479
- log.info(`${response.status} ${decoded}`);
480
522
  writeFileSync(dest, body);
481
523
  written.add(file);
482
524
 
@@ -492,7 +534,12 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
492
534
 
493
535
  prerendered.paths.push(decoded);
494
536
  } else if (response_type !== OK) {
495
- handle_http_error({ status: response.status, path: decoded, referrer, referenceType });
537
+ handle_http_error({
538
+ status: response.status,
539
+ path: decoded,
540
+ referrer,
541
+ referenceType
542
+ });
496
543
  }
497
544
 
498
545
  manifest.assets.add(file);
@@ -28,7 +28,7 @@ export function queue(concurrency) {
28
28
 
29
29
  if (task) {
30
30
  current += 1;
31
- const promise = Promise.resolve(task.fn());
31
+ const promise = (async () => task.fn())(); // could throw synchronously
32
32
 
33
33
  void promise
34
34
  .then(task.fulfil, (err) => {
@@ -54,6 +54,7 @@ export function queue(concurrency) {
54
54
  const promise = new Promise((fulfil, reject) => {
55
55
  tasks.push({ fn, fulfil, reject });
56
56
  });
57
+ promise.catch(() => {});
57
58
 
58
59
  dequeue();
59
60
  return promise;