@sveltejs/kit 3.0.0-next.13 → 3.0.0-next.14

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 (55) hide show
  1. package/package.json +2 -2
  2. package/src/cli.js +2 -2
  3. package/src/core/adapt/builder.js +8 -5
  4. package/src/core/generate_manifest/index.js +6 -0
  5. package/src/core/postbuild/entities.js +8 -2
  6. package/src/core/postbuild/fallback.js +2 -1
  7. package/src/core/postbuild/prerender.js +12 -3
  8. package/src/core/sync/create_manifest_data/conflict.js +1 -1
  9. package/src/core/sync/create_manifest_data/index.js +33 -2
  10. package/src/core/sync/write_app_types.js +72 -27
  11. package/src/core/sync/write_tsconfig/index.js +2 -1
  12. package/src/core/sync/write_types/index.js +5 -2
  13. package/src/exports/node/index.js +2 -7
  14. package/src/exports/public.d.ts +3 -2
  15. package/src/exports/vite/index.js +45 -11
  16. package/src/exports/vite/utils.js +1 -7
  17. package/src/runtime/app/server/remote/query.js +0 -6
  18. package/src/runtime/client/client.js +148 -38
  19. package/src/runtime/client/fetcher.js +2 -13
  20. package/src/runtime/client/remote-functions/form.svelte.js +3 -7
  21. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
  22. package/src/runtime/client/remote-functions/query/index.js +1 -1
  23. package/src/runtime/client/remote-functions/query-batch.svelte.js +1 -1
  24. package/src/runtime/client/remote-functions/query-live/proxy.js +0 -10
  25. package/src/runtime/client/remote-functions/shared.svelte.js +1 -1
  26. package/src/runtime/client/utils.js +1 -0
  27. package/src/runtime/form-utils.js +4 -6
  28. package/src/runtime/pathname.js +37 -0
  29. package/src/runtime/server/fetch.js +5 -8
  30. package/src/runtime/server/index.js +10 -24
  31. package/src/runtime/server/page/crypto.js +2 -2
  32. package/src/runtime/server/page/csp.js +2 -1
  33. package/src/runtime/server/page/load_data.js +2 -29
  34. package/src/runtime/server/page/render.js +19 -1
  35. package/src/runtime/server/page/serialize_data.js +2 -13
  36. package/src/runtime/server/page/server_routing.js +58 -9
  37. package/src/runtime/server/respond.js +20 -27
  38. package/src/runtime/server/state.js +43 -0
  39. package/src/runtime/shared.js +4 -2
  40. package/src/runtime/utils.js +3 -0
  41. package/src/types/ambient-private.d.ts +1 -1
  42. package/src/types/ambient.d.ts +58 -7
  43. package/src/types/global-private.d.ts +1 -1
  44. package/src/types/internal.d.ts +2 -1
  45. package/src/utils/hash.js +21 -0
  46. package/src/utils/http.js +8 -7
  47. package/src/utils/import.js +2 -1
  48. package/src/utils/params.js +1 -1
  49. package/src/utils/regex.js +9 -0
  50. package/src/utils/routing.js +52 -27
  51. package/src/utils/url.js +1 -0
  52. package/src/version.js +1 -1
  53. package/types/index.d.ts +74 -10
  54. package/types/index.d.ts.map +1 -1
  55. package/src/exports/node/polyfills.js +0 -30
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "3.0.0-next.13",
3
+ "version": "3.0.0-next.14",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
@@ -127,7 +127,7 @@
127
127
  },
128
128
  "types": "types/index.d.ts",
129
129
  "engines": {
130
- "node": ">=22"
130
+ "node": ">=22.17"
131
131
  },
132
132
  "scripts": {
133
133
  "lint": "prettier --config ../../.prettierrc --check .",
package/src/cli.js CHANGED
@@ -75,10 +75,10 @@ if (!command) {
75
75
  }
76
76
 
77
77
  if (command === 'sync') {
78
- // create placeholder node_modules/$app/tsconfig/tsconfig.json if necessary, to squelch warnings.
78
+ // create placeholder node_modules/$app/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 dir = 'node_modules/$app/tsconfig';
81
+ const dir = 'node_modules/$app';
82
82
  const base_tsconfig = `${dir}/tsconfig.json`;
83
83
  const base_tsconfig_json = '{}';
84
84
 
@@ -109,16 +109,19 @@ export function create_builder({
109
109
 
110
110
  async compress(directory) {
111
111
  if (!existsSync(directory)) {
112
- return;
112
+ return [];
113
113
  }
114
114
 
115
- const files = list_files(directory, (file) => extensions.includes(extname(file))).map(
116
- (file) => resolve(directory, file)
117
- );
115
+ const files = list_files(directory, (file) => extensions.includes(extname(file)));
118
116
 
119
117
  await Promise.all(
120
- files.flatMap((file) => [compress_file(file, 'gz'), compress_file(file, 'br')])
118
+ files.flatMap((file) => {
119
+ const abs = resolve(directory, file);
120
+ return [compress_file(abs, 'gz'), compress_file(abs, 'br')];
121
+ })
121
122
  );
123
+
124
+ return files;
122
125
  },
123
126
 
124
127
  findServerAssets(route_data) {
@@ -94,6 +94,12 @@ export function generate_manifest({
94
94
  mime_types[ext] ??= mime_lookup(ext) || '';
95
95
  }
96
96
 
97
+ // record extensions that only exist in prerendered output, e.g. a prerendered favicon.ico
98
+ for (const pathname of prerendered) {
99
+ const ext = path.extname(pathname);
100
+ if (ext) mime_types[ext] ??= mime_lookup(ext) || '';
101
+ }
102
+
97
103
  // prettier-ignore
98
104
  // String representation of
99
105
  /** @template {import('@sveltejs/kit').SSRManifest} T */
@@ -2236,7 +2236,7 @@ const entities = {
2236
2236
  'zwnj;': '‌'
2237
2237
  };
2238
2238
 
2239
- const numeric = /&#(x)?([0-9a-f]+);/i;
2239
+ const numeric = /&#(x)?([0-9a-f]+);/gi;
2240
2240
  const named = new RegExp(
2241
2241
  `&(${Object.keys(entities)
2242
2242
  .sort((a, b) => b.length - a.length)
@@ -2247,6 +2247,12 @@ const named = new RegExp(
2247
2247
  /** @param {string} str */
2248
2248
  export function decode(str) {
2249
2249
  return str
2250
- .replace(numeric, (_match, hex, code) => String.fromCharCode(hex ? parseInt(code, 16) : +code))
2250
+ .replace(numeric, (_match, hex, code) => {
2251
+ const codepoint = hex ? parseInt(code, 16) : +code;
2252
+ // mirror the HTML parser, which replaces invalid numeric references with U+FFFD
2253
+ return Number.isInteger(codepoint) && codepoint <= 0x10ffff
2254
+ ? String.fromCodePoint(codepoint)
2255
+ : '\ufffd';
2256
+ })
2251
2257
  .replace(named, (_match, entity) => entities[entity]);
2252
2258
  }
@@ -39,7 +39,8 @@ async function generate_fallback({ manifest_path, env, out_dir, origin, assets }
39
39
  prerendering: {
40
40
  fallback: true,
41
41
  dependencies: new Map(),
42
- remote_responses: new Map()
42
+ remote_responses: new Map(),
43
+ resolved_route_ids: new Set()
43
44
  },
44
45
  read: (file) => readFileSync(join(assets, file))
45
46
  });
@@ -17,6 +17,7 @@ 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
19
  import { log_response } from '../../exports/vite/utils.js';
20
+ import { matches_content_type } from '../../utils/http.js';
20
21
 
21
22
  export default forked(import.meta.url, prerender);
22
23
 
@@ -262,6 +263,9 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
262
263
  /** @type {Map<string, Promise<any>>} */
263
264
  const remote_responses = new Map();
264
265
 
266
+ /** @type {Set<string>} */
267
+ const resolved_route_ids = new Set();
268
+
265
269
  /** @type {Map<string, Set<string>>} */
266
270
  const expected_hashlinks = new Map();
267
271
 
@@ -307,7 +311,8 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
307
311
  },
308
312
  prerendering: {
309
313
  dependencies,
310
- remote_responses
314
+ remote_responses,
315
+ resolved_route_ids
311
316
  },
312
317
  read: (file) => {
313
318
  // stuff we just wrote
@@ -387,7 +392,11 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
387
392
  const headers = Object.fromEntries(response.headers);
388
393
 
389
394
  // if it's a 200 HTML response, crawl it. Skip error responses, as we don't save those
390
- if (response.ok && config.prerender.crawl && headers['content-type'] === 'text/html') {
395
+ if (
396
+ response.ok &&
397
+ config.prerender.crawl &&
398
+ matches_content_type(headers['content-type'], 'text/html')
399
+ ) {
391
400
  const { ids, hrefs, invalid } = crawl(body.toString(), decoded);
392
401
 
393
402
  for (const href of invalid) {
@@ -444,7 +453,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
444
453
  const headers = Object.fromEntries(response.headers);
445
454
 
446
455
  const type = headers['content-type'];
447
- const is_html = response_type === REDIRECT || type === 'text/html';
456
+ const is_html = response_type === REDIRECT || matches_content_type(type, 'text/html');
448
457
 
449
458
  if (!is_html && response.status === 200 && decoded.slice(config.paths.base.length + 1) === '') {
450
459
  throw new Error(
@@ -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 `<?*>`
@@ -41,6 +41,34 @@ export default function create_manifest_data({
41
41
  };
42
42
  }
43
43
 
44
+ /**
45
+ * Whether this route has a `+page`. Independent of `is_endpoint_route` — a route can be both.
46
+ * @param {import('types').RouteData} route
47
+ * @returns {boolean}
48
+ */
49
+ export function is_page_route(route) {
50
+ return !!route.page;
51
+ }
52
+
53
+ /**
54
+ * Whether this route has a `+server`. Independent of `is_page_route` — a route can be both.
55
+ * @param {import('types').RouteData} route
56
+ * @returns {boolean}
57
+ */
58
+ export function is_endpoint_route(route) {
59
+ return !!route.endpoint;
60
+ }
61
+
62
+ /**
63
+ * Whether the router can match this route. `manifest_data.routes` also contains entries for
64
+ * directories with layouts or errors, which never reach `event.route.id`.
65
+ * @param {import('types').RouteData} route
66
+ * @returns {boolean}
67
+ */
68
+ export function is_app_route(route) {
69
+ return is_page_route(route) || is_endpoint_route(route);
70
+ }
71
+
44
72
  /**
45
73
  * Returns a list of files in the `static` directory.
46
74
  * @param {import('types').ValidatedConfig} config
@@ -85,7 +113,7 @@ function resolve_params(config, cwd) {
85
113
  */
86
114
  function create_routes_and_nodes(cwd, config, fallback) {
87
115
  /** @type {import('types').RouteData[]} */
88
- const routes = [];
116
+ let routes = [];
89
117
 
90
118
  const routes_base = posixify(path.relative(cwd, config.kit.files.routes));
91
119
 
@@ -125,7 +153,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
125
153
  );
126
154
  }
127
155
 
128
- return String.fromCharCode(parseInt(code, 16));
156
+ return String.fromCodePoint(parseInt(code, 16));
129
157
  }
130
158
  });
131
159
 
@@ -440,6 +468,9 @@ function create_routes_and_nodes(cwd, config, fallback) {
440
468
  }
441
469
  }
442
470
 
471
+ // remove route objects with no route file
472
+ routes = routes.filter((route) => route.endpoint || route.leaf || route.layout || route.error);
473
+
443
474
  // add parents to error nodes so that we can compute which page options apply to them
444
475
  for (const route of routes) {
445
476
  if (!route.error) continue;
@@ -4,15 +4,42 @@ import { resolve_entry } from '../../utils/filesystem.js';
4
4
  import { posixify } from '../../utils/os.js';
5
5
  import { write_if_changed } from './utils.js';
6
6
  import { s } from '../../utils/misc.js';
7
- import { get_route_segments } from '../../utils/routing.js';
8
-
9
- const replace_optional_params = (/** @type {string} */ id) =>
10
- id.replace(/\/\[\[[^\]]+\]\]/g, '${string}');
11
- const replace_required_params = (/** @type {string} */ id) =>
12
- id.replace(/\/\[[^\]]+\]/g, '/${string}');
13
- /** Convert route ID to pathname by removing layout groups */
14
- const remove_group_segments = (/** @type {string} */ id) => {
15
- return '/' + get_route_segments(id).join('/');
7
+ import {
8
+ decode_escape_sequence,
9
+ encode_pathname_chars,
10
+ get_route_segments,
11
+ segment_pattern
12
+ } from '../../utils/routing.js';
13
+ import { is_app_route, is_endpoint_route, is_page_route } from './create_manifest_data/index.js';
14
+
15
+ const optional_param_pattern = /^\[\[[\w-]+(?:=[\w-]+)?\]\]$/;
16
+ const rest_param_pattern = /^\[\.\.\.[\w-]+(?:=[\w-]+)?\]$/;
17
+
18
+ /**
19
+ * Convert a route ID to the pathnames it can match (relative to the base path), in which each
20
+ * param is replaced with `${string}` and each escape sequence is expanded. A param that fills an
21
+ * entire segment can be absent, so it contributes a pathname with the segment and one without,
22
+ * rather than one that absorbs the `/`
23
+ * @param {string} id
24
+ */
25
+ const get_pathname_patterns = (id) => {
26
+ let pathnames = [''];
27
+
28
+ for (const segment of get_route_segments(id)) {
29
+ const omittable = optional_param_pattern.test(segment) || rest_param_pattern.test(segment);
30
+ const content = omittable
31
+ ? '${string}'
32
+ : segment.replace(segment_pattern, (_, escape_type, escape_code) =>
33
+ escape_type ? encode_pathname_chars(decode_escape_sequence(escape_code)) : '${string}'
34
+ );
35
+
36
+ pathnames = pathnames.flatMap((pathname) => {
37
+ const joined = pathname === '' ? content : `${pathname}/${content}`;
38
+ return omittable ? [joined, pathname] : [joined];
39
+ });
40
+ }
41
+
42
+ return [...new Set(pathnames)];
16
43
  };
17
44
 
18
45
  /**
@@ -142,6 +169,15 @@ function generate_app_types(manifest_data, config, dir) {
142
169
  /** @type {Set<string>} */
143
170
  const pathnames = new Set();
144
171
 
172
+ /** @type {string[]} */
173
+ const page_route_ids = [];
174
+
175
+ /** @type {string[]} */
176
+ const endpoint_route_ids = [];
177
+
178
+ /** @type {string[]} */
179
+ const app_route_ids = [];
180
+
145
181
  /** @type {string[]} */
146
182
  const dynamic_routes = [];
147
183
 
@@ -150,15 +186,17 @@ function generate_app_types(manifest_data, config, dir) {
150
186
 
151
187
  /** @type {Map<string, Map<string, { optional: boolean, matchers: Set<string> | null }>>} */
152
188
  const layout_params_by_route = new Map(
153
- manifest_data.routes.map((route) => [
154
- route.id,
155
- new Map(
156
- route.params.map((p) => [
157
- p.name,
158
- { optional: p.optional, matchers: p.matcher ? new Set([p.matcher]) : null }
159
- ])
160
- )
161
- ])
189
+ manifest_data.routes
190
+ .filter((route) => route.layout)
191
+ .map((route) => [
192
+ route.id,
193
+ new Map(
194
+ route.params.map((p) => [
195
+ p.name,
196
+ { optional: p.optional, matchers: p.matcher ? new Set([p.matcher]) : null }
197
+ ])
198
+ )
199
+ ])
162
200
  );
163
201
 
164
202
  for (const route of manifest_data.routes) {
@@ -192,8 +230,11 @@ function generate_app_types(manifest_data, config, dir) {
192
230
  }
193
231
 
194
232
  for (const route of manifest_data.routes) {
195
- const pathname = remove_group_segments(route.id);
196
- let normalized_pathname = pathname.slice(1);
233
+ const id = s(route.id);
234
+
235
+ if (is_page_route(route)) page_route_ids.push(id);
236
+ if (is_endpoint_route(route)) endpoint_route_ids.push(id);
237
+ if (is_app_route(route)) app_route_ids.push(id);
197
238
 
198
239
  /** @type {(path: string) => string} */
199
240
  let serialise = s;
@@ -203,16 +244,18 @@ function generate_app_types(manifest_data, config, dir) {
203
244
  const type = get_matcher_type(p.matcher);
204
245
  return `${/^\w+$/.test(p.name) ? p.name : `'${p.name}'`}${p.optional ? '?:' : ':'} ${type}${p.optional ? ' | undefined' : ''}`;
205
246
  });
206
- const route_type = `${s(route.id)}: { ${params.join('; ')} }`;
207
247
 
208
- dynamic_routes.push(route_type);
248
+ if (is_app_route(route)) {
249
+ dynamic_routes.push(`${s(route.id)}: { ${params.join('; ')} }`);
250
+ }
209
251
 
210
- normalized_pathname = replace_required_params(replace_optional_params(pathname)).slice(1);
211
252
  serialise = (p) => `\`${p}\` & {}`;
212
253
  }
213
254
 
214
- for (const p of get_pathnames_for_trailing_slash(normalized_pathname, route)) {
215
- pathnames.add(serialise(p));
255
+ for (const pathname of get_pathname_patterns(route.id)) {
256
+ for (const p of get_pathnames_for_trailing_slash(pathname, route)) {
257
+ pathnames.add(serialise(p));
258
+ }
216
259
  }
217
260
 
218
261
  let layout_type = 'Record<string, never>';
@@ -229,7 +272,7 @@ function generate_app_types(manifest_data, config, dir) {
229
272
  if (params.length > 0) layout_type = `{ ${params} }`;
230
273
  }
231
274
 
232
- layouts.push(`${s(route.id)}: ${layout_type}`);
275
+ if (route.layout) layouts.push(`${s(route.id)}: ${layout_type}`);
233
276
  }
234
277
 
235
278
  const assets = manifest_data.assets.map((asset) => s(asset.file));
@@ -237,7 +280,9 @@ function generate_app_types(manifest_data, config, dir) {
237
280
  return [
238
281
  'declare module "$app/types" {',
239
282
  '\texport interface AppTypes {',
240
- `\t\tRouteId(): ${manifest_data.routes.map((r) => s(r.id)).join(' | ')};`,
283
+ `\t\tPageRouteId(): ${page_route_ids.join(' | ') || 'never'};`,
284
+ `\t\tEndpointRouteId(): ${endpoint_route_ids.join(' | ') || 'never'};`,
285
+ `\t\tRouteId(): ${app_route_ids.join(' | ') || 'never'};`,
241
286
  `\t\tRouteParams(): {\n\t\t\t${dynamic_routes.join(';\n\t\t\t')}\n\t\t};`,
242
287
  `\t\tLayoutParams(): {\n\t\t\t${layouts.join(';\n\t\t\t')}\n\t\t};`,
243
288
  `\t\tPath(): ${Array.from(pathnames).join(' | ')};`,
@@ -85,7 +85,8 @@ export function write_tsconfig(kit, root) {
85
85
  * @param {ValidatedKitConfig['typescript']['config']} [transform] TODO get rid of this
86
86
  */
87
87
  function write_parent_tsconfig(root, dir, id, config, example, transform) {
88
- const out_file = path.join(root, `node_modules/${id}/tsconfig.json`);
88
+ // simplified tsconfig resolvers (e.g. Playwright's) only find `${id}.json`, not `${id}/tsconfig.json`
89
+ const out_file = path.join(root, `node_modules/${id}.json`);
89
90
 
90
91
  let normalized = normalize_config(out_file, config);
91
92
  normalized = transform?.(normalized) ?? normalized;
@@ -5,6 +5,7 @@ import { rimraf, walk, resolve_entry } from '../../../utils/filesystem.js';
5
5
  import { compact } from '../../../utils/array.js';
6
6
  import { posixify } from '../../../utils/os.js';
7
7
  import { ts } from '../ts.js';
8
+ import { is_page_route } from '../create_manifest_data/index.js';
8
9
  const remove_relative_parent_traversals = (/** @type {string} */ path) =>
9
10
  path.replace(/\.\.\//g, '');
10
11
  const is_whitespace = (/** @type {string} */ char) => /\s/.test(char);
@@ -278,7 +279,9 @@ function update_types(config, routes, route, root, to_delete = new Set()) {
278
279
  let all_pages_have_load = true;
279
280
  /** @type {import('types').RouteParam[]} */
280
281
  const layout_params = [];
281
- const ids = ['RouteId'];
282
+ // a layout can live in a directory that has no `+page` of its own, in which case its own id
283
+ // is never the matched route — a request to a colocated `+server` doesn't execute layouts
284
+ const ids = is_page_route(route) ? ['RouteId'] : [];
282
285
 
283
286
  route.layout.child_pages?.forEach((page) => {
284
287
  const leaf = routes.get(page);
@@ -312,7 +315,7 @@ function update_types(config, routes, route, root, to_delete = new Set()) {
312
315
  ids.push('null');
313
316
  }
314
317
 
315
- declarations.push(`type LayoutRouteId = ${ids.join(' | ')}`);
318
+ declarations.push(`type LayoutRouteId = ${ids.join(' | ') || 'never'}`);
316
319
 
317
320
  declarations.push(
318
321
  'type LayoutParams = RouteParams & ' + generate_params_type(layout_params, outdir, config)
@@ -137,14 +137,9 @@ export function getRequest({ request, base, bodySizeLimit }) {
137
137
  }
138
138
 
139
139
  const controller = new AbortController();
140
- // TODO: Whenever Node >=22.17 is the minimum supported version, we can do `if (request.readableAborted) controller.abort()` instead
141
- // see https://github.com/nodejs/node/blob/5cf3c3e24c7257a0c6192ed8ef71efec8ddac22b/lib/internal/streams/readable.js#L1443-L1453
142
- let errored = false;
143
- let end_emitted = false;
144
- request.once('error', () => (errored = true));
145
- request.once('end', () => (end_emitted = true));
140
+
146
141
  request.once('close', () => {
147
- if ((errored || request.destroyed) && !end_emitted) {
142
+ if (request.readableAborted) {
148
143
  controller.abort();
149
144
  }
150
145
  });
@@ -253,8 +253,9 @@ export interface Builder {
253
253
  /**
254
254
  * Compress files in `directory` with gzip and brotli, where appropriate. Generates `.gz` and `.br` files alongside the originals.
255
255
  * @param {string} directory The directory containing the files to be compressed
256
+ * @returns an array of the files in `directory` that were compressed
256
257
  */
257
- compress: (directory: string) => Promise<void>;
258
+ compress: (directory: string) => Promise<string[]>;
258
259
  }
259
260
 
260
261
  /**
@@ -862,7 +863,7 @@ export interface KitConfig {
862
863
  * A function that allows you to edit the generated `tsconfig.json`. You can mutate the config (recommended) or return a new one.
863
864
  * This is useful for extending a shared `tsconfig.json` in a monorepo root, for example.
864
865
  *
865
- * Note that any paths configured here should be relative to the generated config file, which is written to `node_modules/$app/tsconfig/tsconfig.json`.
866
+ * Note that any paths configured here should be relative to the generated config file, which is written to `node_modules/$app/tsconfig.json`.
866
867
  *
867
868
  * @default (config) => config
868
869
  * @since 1.3.0
@@ -1,7 +1,7 @@
1
1
  /** @import { EnvVarConfig, KitConfig } from '@sveltejs/kit' */
2
2
  /** @import { Options, SvelteConfig } from '@sveltejs/vite-plugin-svelte' */
3
3
  /** @import { PreprocessorGroup } from 'svelte/compiler' */
4
- /** @import { BuildData, ManifestData, Prerendered, RemoteChunk, RemoteInternals, ServerMetadata, ValidatedConfig, ValidatedKitConfig } from 'types' */
4
+ /** @import { Asset, BuildData, ManifestData, Prerendered, RemoteChunk, RemoteInternals, RouteData, ServerMetadata, ValidatedConfig, ValidatedKitConfig } from 'types' */
5
5
  /** @import { Manifest, Plugin, ResolvedConfig, Rolldown, UserConfig, ViteDevServer } from 'vite' */
6
6
  import fs from 'node:fs';
7
7
  import path from 'node:path';
@@ -46,6 +46,11 @@ import analyse from '../../core/postbuild/analyse.js';
46
46
  import { s } from '../../utils/misc.js';
47
47
  import { hash } from '../../utils/hash.js';
48
48
  import { dedent } from '../../core/sync/utils.js';
49
+ import {
50
+ is_app_route,
51
+ is_endpoint_route,
52
+ is_page_route
53
+ } from '../../core/sync/create_manifest_data/index.js';
49
54
  import { get_import_aliases, get_hash_import_keys } from '../../utils/imports.js';
50
55
  import {
51
56
  app_env_private,
@@ -1121,7 +1126,7 @@ function kit({ svelte_config }) {
1121
1126
  ];
1122
1127
 
1123
1128
  export const assets = [
1124
- ${manifest_data.assets.map((asset) => s({ path: asset.file })).join(',\n')}
1129
+ ${stringify_assets(manifest_data.assets)}
1125
1130
  ];
1126
1131
 
1127
1132
  export const prerendered = [
@@ -1129,7 +1134,7 @@ function kit({ svelte_config }) {
1129
1134
  ];
1130
1135
 
1131
1136
  export const routes = [
1132
- ${manifest_data.routes.map((route) => s({ id: route.id })).join(',\n')}
1137
+ ${stringify_routes(manifest_data.routes)}
1133
1138
  ];
1134
1139
  `;
1135
1140
  }
@@ -1570,7 +1575,7 @@ function kit({ svelte_config }) {
1570
1575
  // the client build and after prerendering respectively.
1571
1576
  replace_manifest_placeholder_variables(server_chunks, `${out}/server`, {
1572
1577
  assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
1573
- routes: manifest_data.routes.map((route) => ({ id: route.id }))
1578
+ routes: get_manifest_routes(manifest_data.routes)
1574
1579
  });
1575
1580
 
1576
1581
  const verbose = builder.config.logLevel === 'info';
@@ -1732,7 +1737,7 @@ function kit({ svelte_config }) {
1732
1737
  replace_manifest_placeholder_variables(client_chunks, `${out}/client`, {
1733
1738
  immutable,
1734
1739
  assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
1735
- routes: manifest_data.routes.map((route) => ({ id: route.id }))
1740
+ routes: get_manifest_routes(manifest_data.routes)
1736
1741
  });
1737
1742
 
1738
1743
  // Now that the client build is done, replace the `build` sentinel
@@ -2112,6 +2117,38 @@ function comparable(value) {
2112
2117
  return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
2113
2118
  }
2114
2119
 
2120
+ /**
2121
+ * @param {Asset[] | undefined} assets
2122
+ * @returns {string}
2123
+ */
2124
+ function stringify_assets(assets) {
2125
+ return assets?.map((asset) => s({ path: asset.file })).join(',\n') ?? '';
2126
+ }
2127
+
2128
+ /**
2129
+ * @param {RouteData[] | undefined} routes
2130
+ * @returns {Array<{ id: string; page: boolean; endpoint: boolean }>}
2131
+ */
2132
+ function get_manifest_routes(routes) {
2133
+ return (
2134
+ routes?.filter(is_app_route).map((route) => ({
2135
+ id: route.id,
2136
+ page: is_page_route(route),
2137
+ endpoint: is_endpoint_route(route)
2138
+ })) ?? []
2139
+ );
2140
+ }
2141
+
2142
+ /**
2143
+ * @param {RouteData[] | undefined} routes
2144
+ * @returns {string}
2145
+ */
2146
+ function stringify_routes(routes) {
2147
+ return get_manifest_routes(routes)
2148
+ .map((route) => s(route))
2149
+ .join(',\n');
2150
+ }
2151
+
2115
2152
  /**
2116
2153
  * Creates the `$app/manifest` data module. During development, real values
2117
2154
  * are emitted for `assets` and `routes` (the only data known at that point).
@@ -2144,20 +2181,17 @@ const create_manifest_data_module = (is_build, manifest_data) => {
2144
2181
  // In dev, `manifest_data` may not be set yet on the very first load,
2145
2182
  // but `configureServer` (which calls `sync.create`) runs before any
2146
2183
  // module is served, so it will be set by the time this is called.
2147
- const routes = manifest_data?.routes.map((route) => s({ id: route.id })).join(',\n') ?? '';
2148
- const assets = manifest_data?.assets.map((asset) => s({ path: asset.file })).join(',\n') ?? '';
2149
-
2150
2184
  return dedent`
2151
2185
  // empty during dev
2152
2186
  export const immutable = [];
2153
2187
  export const prerendered = [];
2154
2188
 
2155
2189
  export const assets = [
2156
- ${assets}
2190
+ ${stringify_assets(manifest_data?.assets)}
2157
2191
  ];
2158
2192
 
2159
2193
  export const routes = [
2160
- ${routes}
2194
+ ${stringify_routes(manifest_data?.routes)}
2161
2195
  ];
2162
2196
  `;
2163
2197
  };
@@ -2173,7 +2207,7 @@ const create_manifest_data_module = (is_build, manifest_data) => {
2173
2207
  * immutable?: Array<{ path: string }>;
2174
2208
  * assets?: Array<{ path: string }>;
2175
2209
  * prerendered?: Array<{ path: string }>;
2176
- * routes?: Array<{ id: string }>;
2210
+ * routes?: Array<{ id: string; page: boolean; endpoint: boolean }>;
2177
2211
  * }} values
2178
2212
  */
2179
2213
  const replace_manifest_placeholder_variables = (chunks, output_dir, values) => {
@@ -2,6 +2,7 @@ import path from 'node:path';
2
2
  import { posixify } from '../../utils/os.js';
3
3
  import { negotiate } from '../../utils/http.js';
4
4
  import { escape_html } from '../../utils/escape.js';
5
+ import { escape_for_regexp } from '../../utils/regex.js';
5
6
  import { stackless } from '../../utils/error.js';
6
7
  import { dedent } from '../../core/sync/utils.js';
7
8
  import { app_server, app_env_private, sveltekit_env_private } from './module_ids.js';
@@ -44,13 +45,6 @@ export function get_config_aliases(config, root) {
44
45
  return alias;
45
46
  }
46
47
 
47
- /**
48
- * @param {string} str
49
- */
50
- function escape_for_regexp(str) {
51
- return str.replace(/[.*+?^${}()|[\]\\]/g, (match) => '\\' + match);
52
- }
53
-
54
48
  /**
55
49
  * Silently respond with 404 for Chrome DevTools workspaces request.
56
50
  * Chrome always requests this at the root, regardless of base path.
@@ -575,12 +575,6 @@ function create_live_query_resource(__, payload, event, state, get_generator) {
575
575
 
576
576
  return Promise.resolve();
577
577
  },
578
- /** @ts-expect-error This method no longer exists */
579
- run() {
580
- throw new Error(
581
- '`.run()` has been removed from live queries. Use `for await (const value of liveQuery())` instead.'
582
- );
583
- },
584
578
  /** @type {Promise<any>['then']} */
585
579
  then(onfulfilled, onrejected) {
586
580
  return get_promise().then(onfulfilled, onrejected);