@sveltejs/kit 3.0.0-next.2 → 3.0.0-next.21

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 (195) hide show
  1. package/package.json +62 -36
  2. package/src/cli.js +15 -12
  3. package/src/constants.js +6 -1
  4. package/src/core/adapt/builder.js +66 -72
  5. package/src/core/adapt/index.js +17 -6
  6. package/src/core/config/index.js +134 -80
  7. package/src/core/config/options.js +295 -273
  8. package/src/core/config/types.d.ts +1 -1
  9. package/src/core/env.js +146 -16
  10. package/src/core/generate_manifest/index.js +18 -15
  11. package/src/core/postbuild/analyse.js +8 -20
  12. package/src/core/postbuild/crawl.js +22 -6
  13. package/src/core/postbuild/entities.js +8 -2
  14. package/src/core/postbuild/fallback.js +4 -2
  15. package/src/core/postbuild/prerender.js +222 -70
  16. package/src/core/postbuild/queue.js +2 -1
  17. package/src/core/sync/create_manifest_data/conflict.js +1 -1
  18. package/src/core/sync/create_manifest_data/index.js +82 -62
  19. package/src/core/sync/sync.js +40 -31
  20. package/src/core/sync/ts.js +1 -1
  21. package/src/core/sync/utils.js +22 -2
  22. package/src/core/sync/{write_non_ambient.js → write_app_types.js} +107 -58
  23. package/src/core/sync/write_client_manifest.js +14 -29
  24. package/src/core/sync/write_env.js +7 -5
  25. package/src/core/sync/write_server.js +37 -45
  26. package/src/core/sync/write_tsconfig/index.js +274 -0
  27. package/src/core/sync/write_tsconfig/test-app/package.json +7 -0
  28. package/src/core/sync/write_tsconfig/utils.js +77 -0
  29. package/src/core/sync/write_tsconfig/validate.js +128 -0
  30. package/src/core/sync/write_types/index.js +127 -123
  31. package/src/core/utils.js +30 -5
  32. package/src/exports/env/index.js +77 -0
  33. package/src/exports/env/public.d.ts +55 -0
  34. package/src/exports/hooks/index.js +3 -9
  35. package/src/exports/hooks/public.d.ts +195 -0
  36. package/src/exports/hooks/sequence.js +9 -6
  37. package/src/exports/index.js +52 -29
  38. package/src/exports/internal/client.js +5 -0
  39. package/src/exports/internal/env.js +8 -5
  40. package/src/exports/internal/index.js +1 -90
  41. package/src/exports/internal/{event.js → server/event.js} +2 -3
  42. package/src/exports/internal/server/index.js +37 -0
  43. package/src/exports/internal/server/telemetry.js +95 -0
  44. package/src/exports/internal/shared.js +90 -0
  45. package/src/exports/node/index.js +64 -22
  46. package/src/exports/params/index.js +70 -0
  47. package/src/exports/params/public.d.ts +63 -0
  48. package/src/exports/public.d.ts +50 -1733
  49. package/src/exports/remote/index.js +11 -0
  50. package/src/exports/remote/public.d.ts +519 -0
  51. package/src/exports/url.js +86 -0
  52. package/src/exports/vite/build/build_server.js +54 -65
  53. package/src/exports/vite/build/remote.js +24 -19
  54. package/src/exports/vite/build/utils.js +0 -8
  55. package/src/exports/vite/dev/index.js +218 -146
  56. package/src/exports/vite/index.js +1590 -858
  57. package/src/exports/vite/module_ids.js +2 -2
  58. package/src/exports/vite/preview/index.js +40 -32
  59. package/src/exports/vite/public.d.ts +588 -0
  60. package/src/exports/vite/utils.js +84 -46
  61. package/src/pathname.js +55 -0
  62. package/src/runner.js +15 -0
  63. package/src/runtime/app/env/internal.js +4 -4
  64. package/src/runtime/app/env/types.d.ts +1 -1
  65. package/src/runtime/app/environment/index.js +3 -3
  66. package/src/runtime/app/{forms.js → forms/index.js} +73 -42
  67. package/src/runtime/app/forms/public.d.ts +2 -0
  68. package/src/runtime/app/forms/types.d.ts +56 -0
  69. package/src/runtime/app/internal/transport.js +53 -0
  70. package/src/runtime/app/manifest/index.js +1 -0
  71. package/src/runtime/app/{navigation.js → navigation/index.js} +2 -1
  72. package/src/runtime/app/navigation/public.d.ts +237 -0
  73. package/src/runtime/app/paths/client.js +37 -37
  74. package/src/runtime/app/paths/index.js +1 -1
  75. package/src/runtime/app/paths/internal/client.js +34 -2
  76. package/src/runtime/app/paths/internal/server.js +6 -23
  77. package/src/runtime/app/paths/internal.d.ts +3 -0
  78. package/src/runtime/app/paths/public.d.ts +1 -29
  79. package/src/runtime/app/paths/server.js +36 -17
  80. package/src/runtime/app/paths/types.d.ts +11 -19
  81. package/src/runtime/app/server/index.js +2 -2
  82. package/src/runtime/app/server/public.d.ts +201 -0
  83. package/src/runtime/app/server/remote/command.js +13 -11
  84. package/src/runtime/app/server/remote/form.js +61 -39
  85. package/src/runtime/app/server/remote/prerender.js +45 -45
  86. package/src/runtime/app/server/remote/query.js +106 -108
  87. package/src/runtime/app/server/remote/requested.js +27 -19
  88. package/src/runtime/app/server/remote/shared.js +76 -76
  89. package/src/runtime/app/service-worker/index.js +24 -0
  90. package/src/runtime/app/state/client.js +4 -2
  91. package/src/runtime/app/state/index.js +7 -5
  92. package/src/runtime/app/state/public.d.ts +72 -0
  93. package/src/runtime/app/state/server.js +3 -0
  94. package/src/runtime/app/stores.js +15 -78
  95. package/src/runtime/client/bundle.js +1 -1
  96. package/src/runtime/client/client-entry.js +3 -0
  97. package/src/runtime/client/client.js +1336 -648
  98. package/src/runtime/client/constants.js +3 -6
  99. package/src/runtime/client/entry.js +24 -3
  100. package/src/runtime/client/fetcher.js +25 -25
  101. package/src/runtime/client/ndjson.js +1 -1
  102. package/src/runtime/client/parse.js +1 -1
  103. package/src/runtime/client/payload.js +17 -0
  104. package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
  105. package/src/runtime/client/remote-functions/command.svelte.js +37 -61
  106. package/src/runtime/client/remote-functions/form.svelte.js +267 -178
  107. package/src/runtime/client/remote-functions/prerender.svelte.js +32 -11
  108. package/src/runtime/client/remote-functions/query/index.js +10 -17
  109. package/src/runtime/client/remote-functions/query/instance.svelte.js +64 -18
  110. package/src/runtime/client/remote-functions/query/proxy.js +5 -5
  111. package/src/runtime/client/remote-functions/query-batch.svelte.js +61 -70
  112. package/src/runtime/client/remote-functions/query-live/index.js +1 -1
  113. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +63 -21
  114. package/src/runtime/client/remote-functions/query-live/iterator.js +15 -12
  115. package/src/runtime/client/remote-functions/query-live/proxy.js +2 -12
  116. package/src/runtime/client/remote-functions/shared.svelte.js +90 -65
  117. package/src/runtime/client/snapshots.js +147 -0
  118. package/src/runtime/client/state.svelte.js +94 -55
  119. package/src/runtime/client/stream.js +3 -2
  120. package/src/runtime/client/types.d.ts +13 -9
  121. package/src/runtime/client/utils.js +22 -110
  122. package/src/runtime/components/root.svelte +56 -0
  123. package/src/runtime/env/dynamic/private.js +7 -0
  124. package/src/runtime/env/dynamic/public.js +7 -0
  125. package/src/runtime/env/static/private.js +6 -0
  126. package/src/runtime/env/static/public.js +6 -0
  127. package/src/runtime/form-utils.js +172 -61
  128. package/src/runtime/pathname.js +20 -32
  129. package/src/runtime/props.svelte.js +72 -0
  130. package/src/runtime/server/constants.js +0 -3
  131. package/src/runtime/server/cookie.js +72 -55
  132. package/src/runtime/server/csrf.js +65 -0
  133. package/src/runtime/server/data/index.js +49 -53
  134. package/src/runtime/server/dev.js +22 -0
  135. package/src/runtime/server/endpoint.js +6 -7
  136. package/src/runtime/server/env_module.js +0 -5
  137. package/src/runtime/server/errors.js +160 -0
  138. package/src/runtime/server/fetch.js +32 -39
  139. package/src/runtime/server/index.js +90 -64
  140. package/src/runtime/server/internal.js +71 -0
  141. package/src/runtime/server/page/actions.js +85 -64
  142. package/src/runtime/server/page/crypto.js +2 -2
  143. package/src/runtime/server/page/csp.js +88 -104
  144. package/src/runtime/server/page/data_serializer.js +24 -25
  145. package/src/runtime/server/page/index.js +36 -53
  146. package/src/runtime/server/page/load_data.js +50 -57
  147. package/src/runtime/server/page/render.js +173 -239
  148. package/src/runtime/server/page/respond_with_error.js +17 -31
  149. package/src/runtime/server/page/serialize_data.js +2 -13
  150. package/src/runtime/server/page/server_routing.js +85 -26
  151. package/src/runtime/server/remote-functions.js +640 -0
  152. package/src/runtime/server/respond.js +190 -131
  153. package/src/runtime/server/sourcemaps.js +183 -0
  154. package/src/runtime/server/state.js +53 -0
  155. package/src/runtime/server/utils.js +13 -155
  156. package/src/runtime/shared.js +20 -40
  157. package/src/runtime/utils.js +3 -0
  158. package/src/types/ambient-private.d.ts +11 -1
  159. package/src/types/ambient.d.ts +87 -36
  160. package/src/types/global-private.d.ts +25 -25
  161. package/src/types/internal.d.ts +163 -157
  162. package/src/types/private.d.ts +41 -1
  163. package/src/utils/error.js +28 -4
  164. package/src/utils/escape.js +9 -25
  165. package/src/utils/features.js +1 -1
  166. package/src/utils/filesystem.js +1 -23
  167. package/src/utils/fork.js +7 -2
  168. package/src/utils/hash.js +21 -0
  169. package/src/utils/http.js +8 -7
  170. package/src/utils/import.js +9 -2
  171. package/src/utils/imports.js +83 -0
  172. package/src/utils/mime.js +9 -0
  173. package/src/utils/page_nodes.js +6 -7
  174. package/src/utils/params.js +67 -0
  175. package/src/utils/regex.js +9 -0
  176. package/src/utils/routing.js +145 -73
  177. package/src/utils/shared-iterator.js +5 -0
  178. package/src/utils/streaming.js +14 -4
  179. package/src/utils/url.js +33 -2
  180. package/src/version.js +1 -1
  181. package/types/index.d.ts +2616 -2433
  182. package/types/index.d.ts.map +120 -106
  183. package/src/core/sync/write_ambient.js +0 -18
  184. package/src/core/sync/write_root.js +0 -148
  185. package/src/core/sync/write_tsconfig.js +0 -250
  186. package/src/exports/internal/server.js +0 -22
  187. package/src/exports/node/polyfills.js +0 -30
  188. package/src/runtime/server/app.js +0 -9
  189. package/src/runtime/server/remote.js +0 -457
  190. package/src/runtime/shared-server.js +0 -7
  191. package/src/runtime/telemetry/otel.js +0 -21
  192. package/src/runtime/telemetry/record_span.js +0 -65
  193. package/src/types/synthetic/$lib.md +0 -5
  194. /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
  195. /package/src/{runtime/telemetry/noop.js → telemetry.js} +0 -0
@@ -1,8 +1,9 @@
1
- /** @import { Adapter } from '@sveltejs/kit' */
2
- import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
1
+ import process from 'node:process';
2
+ import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
+ import { clearLine, moveCursor } from 'node:readline';
4
5
  import { pathToFileURL } from 'node:url';
5
- import { mkdirp, walk } from '../../utils/filesystem.js';
6
+ import { walk } from '../../utils/filesystem.js';
6
7
  import { posixify } from '../../utils/os.js';
7
8
  import { noop } from '../../utils/functions.js';
8
9
  import { decode_uri, is_root_relative, resolve } from '../../utils/url.js';
@@ -17,6 +18,7 @@ import * as devalue from 'devalue';
17
18
  import { createReadableStream } from '@sveltejs/kit/node';
18
19
  import generate_fallback from './fallback.js';
19
20
  import { stringify_remote_arg } from '../../runtime/shared.js';
21
+ import { matches_content_type } from '../../utils/http.js';
20
22
 
21
23
  export default forked(import.meta.url, prerender);
22
24
 
@@ -35,46 +37,94 @@ const SPECIAL_HASHLINKS = new Set(['', 'top']);
35
37
  * verbose: boolean;
36
38
  * env: Record<string, string>;
37
39
  * vite_config_file: string | undefined;
40
+ * is_tty: boolean | undefined;
38
41
  * }} opts
39
42
  */
40
- async function prerender({ hash, out, manifest_path, metadata, verbose, env, vite_config_file }) {
43
+ async function prerender({
44
+ hash,
45
+ out,
46
+ manifest_path,
47
+ metadata,
48
+ verbose,
49
+ env,
50
+ vite_config_file,
51
+ is_tty
52
+ }) {
41
53
  /** @type {import('@sveltejs/kit').SSRManifest} */
42
54
  const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
43
55
 
44
56
  /** @type {import('types').ServerInternalModule} */
45
- const internal = await import(pathToFileURL(`${out}/server/internal.js`).href);
57
+ const { set_building, set_prerendering, set_manifest, set_read_implementation, log_response } =
58
+ await import(pathToFileURL(`${out}/server/internal.js`).href);
59
+
60
+ // configure `import { building } from `$app/env` —
61
+ // essential we do this before analysing the code
62
+ set_building();
63
+ set_prerendering();
64
+
65
+ // `set_env` and `Server` live in modules that import the user's `src/env` config. We import them
66
+ // *after* `set_building()` so that `building`-dependent expressions resolve correctly
67
+ /** @type {typeof import('__sveltekit/env')} */
68
+ const { set_env } = await import(pathToFileURL(`${out}/server/env.js`).href);
69
+ set_env(env);
46
70
 
47
71
  /** @type {import('types').ServerModule} */
48
72
  const { Server } = await import(pathToFileURL(`${out}/server/index.js`).href);
49
73
 
50
- // configure `import { building } from `$app/env` —
51
- // essential we do this before analysing the code
52
- internal.set_building();
53
- internal.set_prerendering();
74
+ const throw_handled = () => {
75
+ throw new Error('__handled__');
76
+ };
54
77
 
55
78
  /**
56
79
  * @template {{message: string}} T
57
80
  * @template {Omit<T, 'message'>} K
58
- * @param {import('types').Logger} log
59
- * @param {'fail' | 'warn' | 'ignore' | ((details: T) => void)} input
81
+ * @param {string} name
82
+ * @param {'fail' | 'warn' | 'ignore' | undefined | ((details: T) => void)} input
60
83
  * @param {(details: K) => string} format
61
- * @returns {(details: K) => void}
84
+ * @returns {(details: K & { error?: unknown }) => void}
62
85
  */
63
- function normalise_error_handler(log, input, format) {
86
+ function normalise_error_handler(name, input, format) {
87
+ /**
88
+ * @param {any} details
89
+ */
90
+ function log_failure(details) {
91
+ const message = format(details);
92
+ log.error(`\n${message}\n`);
93
+ }
94
+
64
95
  switch (input) {
65
96
  case 'fail':
66
97
  return (details) => {
67
- throw new Error(format(details));
98
+ log_failure(details);
99
+ throw_handled();
68
100
  };
69
101
  case 'warn':
70
- return (details) => {
71
- log.error(format(details));
72
- };
102
+ return log_failure;
73
103
  case 'ignore':
74
104
  return noop;
105
+ case undefined: {
106
+ return (details) => {
107
+ log_failure(details);
108
+
109
+ log.err(
110
+ `To suppress or handle this error, implement \`${name}\` in https://svelte.dev/docs/kit/configuration#prerender\n`
111
+ );
112
+
113
+ throw_handled();
114
+ };
115
+ }
75
116
  default:
76
- // @ts-expect-error TS thinks T might be of a different kind, but it's not
77
- return (details) => input({ ...details, message: format(details) });
117
+ return (details) => {
118
+ const message = format(details);
119
+
120
+ try {
121
+ // @ts-expect-error TS thinks T might be of a different kind, but it's not
122
+ input({ ...details, message });
123
+ } catch (error) {
124
+ log.prettyError(error, import.meta.dirname);
125
+ throw_handled();
126
+ }
127
+ };
78
128
  }
79
129
  }
80
130
 
@@ -103,21 +153,23 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
103
153
 
104
154
  const vite_config = await load_vite_config(vite_config_file);
105
155
 
106
- const config = extract_svelte_config(vite_config).kit;
156
+ const config = extract_svelte_config(vite_config);
157
+
158
+ const prerender_origin = config.paths.origin || 'http://sveltekit-prerender';
107
159
 
108
160
  if (hash) {
109
161
  const fallback = await generate_fallback({
110
162
  manifest_path,
111
163
  env,
112
164
  out_dir: config.outDir,
113
- origin: config.prerender.origin,
165
+ origin: prerender_origin,
114
166
  assets: config.files.assets
115
167
  });
116
168
 
117
169
  const file = output_filename('/', true);
118
170
  const dest = `${config.outDir}/output/prerendered/pages/${file}`;
119
171
 
120
- mkdirp(dirname(dest));
172
+ mkdirSync(dirname(dest), { recursive: true });
121
173
  writeFileSync(dest, fallback);
122
174
 
123
175
  prerendered.pages.set('/', { file });
@@ -125,13 +177,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
125
177
  return { prerendered, prerender_map };
126
178
  }
127
179
 
128
- // TODO this can just be config.adapter?
129
- /** @type {Adapter | undefined} */
130
- const adapter = vite_config.plugins.find(
131
- (plugin) => plugin.name === 'vite-plugin-sveltekit-adapter'
132
- )?.api?.adapter;
133
-
134
- const emulator = await adapter?.emulate?.();
180
+ const emulator = await config.adapter?.emulate?.();
135
181
 
136
182
  /** @type {import('types').Logger} */
137
183
  const log = logger({ verbose });
@@ -140,43 +186,58 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
140
186
  const saved = new Map();
141
187
 
142
188
  const handle_http_error = normalise_error_handler(
143
- log,
189
+ 'handleHttpError',
144
190
  config.prerender.handleHttpError,
145
191
  ({ status, path, referrer, referenceType }) => {
146
- const message =
147
- status === 404 && !path.startsWith(config.paths.base)
148
- ? `${path} does not begin with \`base\`, which is configured in \`paths.base\` and can be imported from \`$app/paths\` - see https://svelte.dev/docs/kit/configuration#paths for more info`
149
- : path;
192
+ let message = `Failed to prerender ${path}`;
193
+
194
+ if (status === 404) {
195
+ if (!path.startsWith(config.paths.base)) {
196
+ message = referrer ? `${path} (${referenceType} from ${referrer})` : path;
197
+
198
+ 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\``;
199
+ } else if (referrer) {
200
+ message = `${path} was ${referenceType} from ${referrer}`;
201
+ }
202
+ }
150
203
 
151
- return `${status} ${message}${referrer ? ` (${referenceType} from ${referrer})` : ''}`;
204
+ return message;
152
205
  }
153
206
  );
154
207
 
155
208
  const handle_missing_id = normalise_error_handler(
156
- log,
209
+ 'handleMissingId',
157
210
  config.prerender.handleMissingId,
158
211
  ({ path, id, referrers }) => {
159
212
  return (
160
- `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:` +
213
+ `The following pages contain links to ${path}#${id}, but no element with id="${id}" exists on ${path}:` +
161
214
  referrers.map((l) => `\n - ${l}`).join('')
162
215
  );
163
216
  }
164
217
  );
165
218
 
166
219
  const handle_entry_generator_mismatch = normalise_error_handler(
167
- log,
220
+ 'handleEntryGeneratorMismatch',
168
221
  config.prerender.handleEntryGeneratorMismatch,
169
222
  ({ generatedFromId, entry, matchedId }) => {
170
- 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.`;
223
+ return `The entries export from ${generatedFromId} generated entry ${entry}, which was matched by ${matchedId === entry ? 'a static route' : matchedId}`;
171
224
  }
172
225
  );
173
226
 
174
227
  const handle_not_prerendered_route = normalise_error_handler(
175
- log,
228
+ 'handleUnseenRoutes',
176
229
  config.prerender.handleUnseenRoutes,
177
230
  ({ routes }) => {
178
231
  const list = routes.map((id) => ` - ${id}`).join('\n');
179
- 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.`;
232
+ return `The following routes were marked as prerenderable, but were not prerendered because they were not found while crawling your app:\n${list}`;
233
+ }
234
+ );
235
+
236
+ const handle_invalid_url = normalise_error_handler(
237
+ 'handleInvalidUrl',
238
+ config.prerender.handleInvalidUrl,
239
+ ({ href, referrer }) => {
240
+ return `Invalid URL ${href}${referrer ? ` (linked from ${referrer})` : ''}`;
180
241
  }
181
242
  );
182
243
 
@@ -210,12 +271,73 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
210
271
 
211
272
  const seen = new Set();
212
273
  const written = new Set();
274
+
275
+ /** @type {Map<string, Promise<any>>} */
213
276
  const remote_responses = new Map();
214
277
 
278
+ /** @type {null | { clear: () => void; update: (path: string) => void; updated: number }} */
279
+ let progress = null;
280
+
281
+ if (is_tty) {
282
+ // Where possible, provide progress feedback by showing the path we're
283
+ // currently requesting, then clearing the line once the response comes in.
284
+ // This avoids the wall of text that happens when you prerender
285
+ // many pages and log each response
286
+ let current = false;
287
+ let mid_line = false;
288
+ const stdout_write = process.stdout.write;
289
+ const stderr_write = process.stderr.write;
290
+
291
+ /** @type {ProxyHandler<typeof stdout_write>} */
292
+ const track_output = {
293
+ apply(target, this_arg, args) {
294
+ const chunk = args[0];
295
+ if (chunk.length > 0) {
296
+ current = false;
297
+ mid_line =
298
+ typeof chunk === 'string' ? !chunk.endsWith('\n') : chunk[chunk.length - 1] !== 10;
299
+ }
300
+ return Reflect.apply(target, this_arg, args);
301
+ }
302
+ };
303
+
304
+ process.stdout.write = new Proxy(stdout_write, track_output);
305
+ process.stderr.write = new Proxy(stderr_write, track_output);
306
+
307
+ progress = {
308
+ clear: () => {
309
+ // If app code writes to stdout or stderr, don't move the cursor to clear
310
+ // the previous progress log, because that will corrupt things
311
+ if (!current) return;
312
+
313
+ moveCursor(process.stdout, 0, -1);
314
+ clearLine(process.stdout, 0);
315
+ },
316
+
317
+ update: (path) => {
318
+ if (mid_line) {
319
+ // app output ended mid-line — start a fresh one rather than appending to it
320
+ stdout_write.call(process.stdout, '\n');
321
+ }
322
+
323
+ stdout_write.call(process.stdout, `crawling ${path}\n`);
324
+ current = true;
325
+ mid_line = false;
326
+ },
327
+
328
+ updated: 0
329
+ };
330
+
331
+ console.log('');
332
+ }
333
+
334
+ /** @type {Set<string>} */
335
+ const resolved_route_ids = new Set();
336
+
215
337
  /** @type {Map<string, Set<string>>} */
216
338
  const expected_hashlinks = new Map();
217
339
 
218
- /** @type {Map<string, string[]>} */
340
+ /** @type {Map<string, Set<string>>} */
219
341
  const actual_hashlinks = new Map();
220
342
 
221
343
  /**
@@ -249,13 +371,28 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
249
371
  /** @type {Map<string, import('types').PrerenderDependency>} */
250
372
  const dependencies = new Map();
251
373
 
252
- const response = await server.respond(new Request(config.prerender.origin + encoded), {
374
+ if (progress) {
375
+ progress.clear();
376
+ progress.update(decoded);
377
+
378
+ if (Date.now() - progress.updated > 50) {
379
+ progress.updated = Date.now();
380
+
381
+ // without this, the update will rarely be visible, and progress will appear stuck
382
+ await new Promise((f) => setTimeout(f, 0));
383
+ }
384
+ }
385
+
386
+ const request = new Request(prerender_origin + encoded);
387
+
388
+ const response = await server.respond(request, {
253
389
  getClientAddress() {
254
390
  throw new Error('Cannot read clientAddress during prerendering');
255
391
  },
256
392
  prerendering: {
257
393
  dependencies,
258
- remote_responses
394
+ remote_responses,
395
+ resolved_route_ids
259
396
  },
260
397
  read: (file) => {
261
398
  // stuff we just wrote
@@ -287,6 +424,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
287
424
  });
288
425
  }
289
426
 
427
+ if (response.status >= 400) {
428
+ log_response(response.status, request);
429
+ }
430
+
290
431
  const body = Buffer.from(await response.arrayBuffer());
291
432
 
292
433
  const category = decoded.startsWith(remote_prefix) ? 'data' : 'pages';
@@ -331,17 +472,25 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
331
472
  const headers = Object.fromEntries(response.headers);
332
473
 
333
474
  // if it's a 200 HTML response, crawl it. Skip error responses, as we don't save those
334
- if (response.ok && config.prerender.crawl && headers['content-type'] === 'text/html') {
335
- const { ids, hrefs } = crawl(body.toString(), decoded);
475
+ if (
476
+ response.ok &&
477
+ config.prerender.crawl &&
478
+ matches_content_type(headers['content-type'], 'text/html')
479
+ ) {
480
+ const { ids, hrefs, invalid } = crawl(body.toString(), decoded);
336
481
 
337
- actual_hashlinks.set(decoded, ids);
482
+ for (const href of invalid) {
483
+ handle_invalid_url({ href, referrer: decoded });
484
+ }
485
+
486
+ actual_hashlinks.set(decoded, new Set(ids));
338
487
 
339
488
  /** @param {string} href */
340
489
  const removePrerenderOrigin = (href) => {
341
- if (href.startsWith(config.prerender.origin)) {
342
- if (href === config.prerender.origin) return '/';
343
- if (href.at(config.prerender.origin.length) !== '/') return href;
344
- return href.slice(config.prerender.origin.length);
490
+ if (href.startsWith(prerender_origin)) {
491
+ if (href === prerender_origin) return '/';
492
+ if (href.at(prerender_origin.length) !== '/') return href;
493
+ return href.slice(prerender_origin.length);
345
494
  }
346
495
  return href;
347
496
  };
@@ -384,7 +533,13 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
384
533
  const headers = Object.fromEntries(response.headers);
385
534
 
386
535
  const type = headers['content-type'];
387
- const is_html = response_type === REDIRECT || type === 'text/html';
536
+ const is_html = response_type === REDIRECT || matches_content_type(type, 'text/html');
537
+
538
+ if (!is_html && response.status === 200 && decoded.slice(config.paths.base.length + 1) === '') {
539
+ throw new Error(
540
+ `Cannot prerender a root +server.js that returns a non-HTML response - static hosts always serve an HTML file for \`${config.paths.base || '/'}\``
541
+ );
542
+ }
388
543
 
389
544
  const file = output_filename(decoded, is_html);
390
545
  const dest = `${config.outDir}/output/prerendered/${category}/${file}`;
@@ -405,9 +560,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
405
560
  }
406
561
 
407
562
  if (!headers['x-sveltekit-normalize']) {
408
- mkdirp(dirname(dest));
409
-
410
- log.warn(`${response.status} ${decoded} -> ${location}`);
563
+ mkdirSync(dirname(dest), { recursive: true });
411
564
 
412
565
  writeFileSync(
413
566
  dest,
@@ -453,9 +606,8 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
453
606
  );
454
607
  }
455
608
 
456
- mkdirp(dir);
609
+ mkdirSync(dir, { recursive: true });
457
610
 
458
- log.info(`${response.status} ${decoded}`);
459
611
  writeFileSync(dest, body);
460
612
  written.add(file);
461
613
 
@@ -471,7 +623,12 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
471
623
 
472
624
  prerendered.paths.push(decoded);
473
625
  } else if (response_type !== OK) {
474
- handle_http_error({ status: response.status, path: decoded, referrer, referenceType });
626
+ handle_http_error({
627
+ status: response.status,
628
+ path: decoded,
629
+ referrer,
630
+ referenceType
631
+ });
475
632
  }
476
633
 
477
634
  manifest.assets.add(file);
@@ -495,12 +652,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
495
652
  }
496
653
  }
497
654
 
498
- // the user's remote function modules may reference environment variables,
499
- // `read` or the `manifest` at the top-level so we need to set them before
500
- // evaluating those modules to avoid potential runtime errors
501
- internal.set_env(env);
502
- internal.set_manifest(manifest);
503
- internal.set_read_implementation((file) => createReadableStream(`${out}/server/${file}`));
655
+ // the user's remote function modules may reference `read` or the `manifest` at the top-level
656
+ // so we need to set them before evaluating those modules to avoid potential runtime errors
657
+ set_manifest(manifest);
658
+ set_read_implementation((file) => createReadableStream(`${out}/server/${file}`));
504
659
 
505
660
  /** @type {Array<import('types').RemotePrerenderInternals>} */
506
661
  const prerender_functions = [];
@@ -554,14 +709,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
554
709
  }
555
710
  }
556
711
 
557
- const transport = (await internal.get_hooks()).transport ?? {};
558
712
  for (const internals of prerender_functions) {
559
713
  if (internals.has_arg) {
560
714
  for (const arg of (await internals.inputs?.()) ?? []) {
561
- void enqueue(
562
- null,
563
- remote_prefix + internals.id + '/' + stringify_remote_arg(arg, transport)
564
- );
715
+ void enqueue(null, remote_prefix + internals.id + '/' + stringify_remote_arg(arg));
565
716
  }
566
717
  } else {
567
718
  void enqueue(null, remote_prefix + internals.id);
@@ -569,6 +720,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
569
720
  }
570
721
 
571
722
  await q.done();
723
+ progress?.clear();
572
724
 
573
725
  // handle invalid fragment links
574
726
  for (const [key, referrers] of expected_hashlinks) {
@@ -580,7 +732,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
580
732
  // ignore fragment links to pages that were not prerendered
581
733
  if (!hashlinks) continue;
582
734
 
583
- if (!hashlinks.includes(id) && !SPECIAL_HASHLINKS.has(id)) {
735
+ if (!hashlinks.has(id) && !SPECIAL_HASHLINKS.has(id)) {
584
736
  handle_missing_id({ id, path, referrers: Array.from(referrers) });
585
737
  }
586
738
  }
@@ -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;
@@ -60,7 +60,7 @@ function normalize_route_id(id) {
60
60
  .replace(/(?<=^|\/)\(.+?\)(?=$|\/)/g, '')
61
61
 
62
62
  .replace(/\[[ux]\+([0-9a-f]+)\]/g, (_, x) =>
63
- String.fromCharCode(parseInt(x, 16)).replace(/\//g, '%2f')
63
+ String.fromCodePoint(parseInt(x, 16)).replace(/\//g, '%2f')
64
64
  )
65
65
 
66
66
  // replace `[param]` with `<*>`, `[param=x]` with `<x>`, and `[[param]]` with `<?*>`