@sveltejs/kit 3.0.0-next.12 → 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.
- package/package.json +11 -11
- package/src/cli.js +2 -2
- package/src/core/adapt/builder.js +8 -5
- package/src/core/generate_manifest/index.js +6 -0
- package/src/core/postbuild/entities.js +8 -2
- package/src/core/postbuild/fallback.js +2 -1
- package/src/core/postbuild/prerender.js +15 -6
- package/src/core/sync/create_manifest_data/conflict.js +1 -1
- package/src/core/sync/create_manifest_data/index.js +33 -2
- package/src/core/sync/write_app_types.js +73 -29
- package/src/core/sync/write_tsconfig/index.js +15 -2
- package/src/core/sync/write_types/index.js +5 -2
- package/src/exports/index.js +23 -12
- package/src/exports/internal/shared.js +4 -12
- package/src/exports/node/index.js +2 -7
- package/src/exports/public.d.ts +61 -12
- package/src/exports/vite/build/remote.js +10 -12
- package/src/exports/vite/dev/index.js +32 -19
- package/src/exports/vite/index.js +162 -133
- package/src/exports/vite/utils.js +1 -7
- package/src/runtime/app/server/remote/form.js +9 -1
- package/src/runtime/app/server/remote/prerender.js +13 -9
- package/src/runtime/app/server/remote/query.js +0 -6
- package/src/runtime/app/server/remote/requested.js +4 -4
- package/src/runtime/app/state/client.js +3 -0
- package/src/runtime/app/state/index.js +2 -2
- package/src/runtime/app/state/server.js +3 -0
- package/src/runtime/client/client.js +731 -342
- package/src/runtime/client/constants.js +2 -6
- package/src/runtime/client/fetcher.js +24 -25
- package/src/runtime/client/remote-functions/form.svelte.js +42 -14
- package/src/runtime/client/remote-functions/prerender.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query/index.js +1 -1
- package/src/runtime/client/remote-functions/query/instance.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query-batch.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query-live/iterator.js +10 -8
- package/src/runtime/client/remote-functions/query-live/proxy.js +0 -10
- package/src/runtime/client/remote-functions/shared.svelte.js +12 -11
- package/src/runtime/client/state.svelte.js +10 -22
- package/src/runtime/client/types.d.ts +1 -2
- package/src/runtime/client/utils.js +22 -14
- package/src/runtime/components/root.svelte +4 -14
- package/src/runtime/form-utils.js +4 -6
- package/src/runtime/pathname.js +37 -0
- package/src/runtime/props.svelte.js +71 -0
- package/src/runtime/server/fetch.js +9 -14
- package/src/runtime/server/index.js +10 -24
- package/src/runtime/server/page/crypto.js +2 -2
- package/src/runtime/server/page/csp.js +88 -104
- package/src/runtime/server/page/index.js +1 -2
- package/src/runtime/server/page/load_data.js +16 -33
- package/src/runtime/server/page/render.js +46 -25
- package/src/runtime/server/page/respond_with_error.js +1 -3
- package/src/runtime/server/page/serialize_data.js +2 -13
- package/src/runtime/server/page/server_routing.js +58 -9
- package/src/runtime/server/respond.js +20 -29
- package/src/runtime/server/state.js +43 -0
- package/src/runtime/shared.js +4 -2
- package/src/runtime/utils.js +3 -0
- package/src/types/ambient-private.d.ts +2 -2
- package/src/types/ambient.d.ts +59 -8
- package/src/types/global-private.d.ts +1 -1
- package/src/types/internal.d.ts +4 -8
- package/src/types/private.d.ts +3 -12
- package/src/utils/hash.js +21 -0
- package/src/utils/http.js +8 -7
- package/src/utils/import.js +2 -1
- package/src/utils/params.js +1 -1
- package/src/utils/regex.js +9 -0
- package/src/utils/routing.js +52 -27
- package/src/utils/url.js +1 -0
- package/src/version.js +1 -1
- package/types/index.d.ts +165 -66
- package/types/index.d.ts.map +2 -1
- package/src/exports/node/polyfills.js +0 -30
- package/src/runtime/types.d.ts +0 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltejs/kit",
|
|
3
|
-
"version": "3.0.0-next.
|
|
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",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@opentelemetry/api": "^1.9.0",
|
|
33
|
-
"@playwright/test": "
|
|
33
|
+
"@playwright/test": "~1.61.0",
|
|
34
34
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
35
35
|
"@types/connect": "^3.4.38",
|
|
36
36
|
"@types/node": "^22.19.19",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"svelte": "^5.56.3",
|
|
41
41
|
"svelte-preprocess": "^6.0.5",
|
|
42
42
|
"typescript": "~6.0.3",
|
|
43
|
-
"valibot": "^1.2
|
|
43
|
+
"valibot": "^1.4.2",
|
|
44
44
|
"vite": "^8.1.5",
|
|
45
45
|
"vitest": "^4.1.10"
|
|
46
46
|
},
|
|
@@ -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 .",
|
|
@@ -136,15 +136,15 @@
|
|
|
136
136
|
"format": "prettier --config ../../.prettierrc --write .",
|
|
137
137
|
"test": "pnpm test:unit && pnpm test:integration",
|
|
138
138
|
"test:integration": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test",
|
|
139
|
-
"test:
|
|
140
|
-
"test:
|
|
139
|
+
"test:dev": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:dev",
|
|
140
|
+
"test:build": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:build",
|
|
141
|
+
"test:cross-platform:dev": "pnpm test:unit && pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:dev",
|
|
142
|
+
"test:cross-platform:build": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:build",
|
|
141
143
|
"test:server-side-route-resolution:dev": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:server-side-route-resolution:dev",
|
|
142
|
-
"test:server-side-route-resolution:build": "pnpm
|
|
144
|
+
"test:server-side-route-resolution:build": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:server-side-route-resolution:build",
|
|
143
145
|
"test:svelte-async:dev": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:svelte-async:dev",
|
|
144
|
-
"test:svelte-async:build": "pnpm
|
|
145
|
-
"test:unit
|
|
146
|
-
"test:unit:prod": "NODE_ENV=production vitest --config kit.vitest.config.js run csp.spec.js cookie.spec.js",
|
|
147
|
-
"test:unit": "pnpm test:unit:dev && pnpm test:unit:prod",
|
|
146
|
+
"test:svelte-async:build": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:svelte-async:build",
|
|
147
|
+
"test:unit": "vitest run ./",
|
|
148
148
|
"generate:version": "node scripts/generate-version.js",
|
|
149
149
|
"generate:types": "node scripts/generate-dts.js"
|
|
150
150
|
}
|
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
|
|
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
|
|
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)))
|
|
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) =>
|
|
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]+);/
|
|
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) =>
|
|
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,10 +263,13 @@ 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
|
|
|
268
|
-
/** @type {Map<string, string
|
|
272
|
+
/** @type {Map<string, Set<string>>} */
|
|
269
273
|
const actual_hashlinks = new Map();
|
|
270
274
|
|
|
271
275
|
/**
|
|
@@ -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,14 +392,18 @@ 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 (
|
|
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) {
|
|
394
403
|
handle_invalid_url({ href, referrer: decoded });
|
|
395
404
|
}
|
|
396
405
|
|
|
397
|
-
actual_hashlinks.set(decoded, ids);
|
|
406
|
+
actual_hashlinks.set(decoded, new Set(ids));
|
|
398
407
|
|
|
399
408
|
/** @param {string} href */
|
|
400
409
|
const removePrerenderOrigin = (href) => {
|
|
@@ -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
|
|
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(
|
|
@@ -646,7 +655,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
646
655
|
// ignore fragment links to pages that were not prerendered
|
|
647
656
|
if (!hashlinks) continue;
|
|
648
657
|
|
|
649
|
-
if (!hashlinks.
|
|
658
|
+
if (!hashlinks.has(id) && !SPECIAL_HASHLINKS.has(id)) {
|
|
650
659
|
handle_missing_id({ id, path, referrers: Array.from(referrers) });
|
|
651
660
|
}
|
|
652
661
|
}
|
|
@@ -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.
|
|
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
|
-
|
|
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.
|
|
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 {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
/**
|
|
@@ -53,8 +80,6 @@ function get_pathnames_for_trailing_slash(pathname, route) {
|
|
|
53
80
|
const template = `
|
|
54
81
|
declare module "svelte/elements" {
|
|
55
82
|
export interface HTMLAttributes<T> {
|
|
56
|
-
'data-sveltekit-keepfocus'?: true | false | '' | undefined | null;
|
|
57
|
-
'data-sveltekit-noscroll'?: true | false | '' | undefined | null;
|
|
58
83
|
'data-sveltekit-preload-code'?:
|
|
59
84
|
| true
|
|
60
85
|
| false
|
|
@@ -68,6 +93,7 @@ declare module "svelte/elements" {
|
|
|
68
93
|
'data-sveltekit-preload-data'?: true | false | '' | 'hover' | 'tap' | undefined | null;
|
|
69
94
|
'data-sveltekit-reload'?: true | false | '' | undefined | null;
|
|
70
95
|
'data-sveltekit-replacestate'?: true | false | '' | undefined | null;
|
|
96
|
+
'data-sveltekit-reset'?: true | false | '' | undefined | null;
|
|
71
97
|
}
|
|
72
98
|
}
|
|
73
99
|
`;
|
|
@@ -143,6 +169,15 @@ function generate_app_types(manifest_data, config, dir) {
|
|
|
143
169
|
/** @type {Set<string>} */
|
|
144
170
|
const pathnames = new Set();
|
|
145
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
|
+
|
|
146
181
|
/** @type {string[]} */
|
|
147
182
|
const dynamic_routes = [];
|
|
148
183
|
|
|
@@ -151,15 +186,17 @@ function generate_app_types(manifest_data, config, dir) {
|
|
|
151
186
|
|
|
152
187
|
/** @type {Map<string, Map<string, { optional: boolean, matchers: Set<string> | null }>>} */
|
|
153
188
|
const layout_params_by_route = new Map(
|
|
154
|
-
manifest_data.routes
|
|
155
|
-
route.
|
|
156
|
-
|
|
157
|
-
route.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
+
])
|
|
163
200
|
);
|
|
164
201
|
|
|
165
202
|
for (const route of manifest_data.routes) {
|
|
@@ -193,8 +230,11 @@ function generate_app_types(manifest_data, config, dir) {
|
|
|
193
230
|
}
|
|
194
231
|
|
|
195
232
|
for (const route of manifest_data.routes) {
|
|
196
|
-
const
|
|
197
|
-
|
|
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);
|
|
198
238
|
|
|
199
239
|
/** @type {(path: string) => string} */
|
|
200
240
|
let serialise = s;
|
|
@@ -204,16 +244,18 @@ function generate_app_types(manifest_data, config, dir) {
|
|
|
204
244
|
const type = get_matcher_type(p.matcher);
|
|
205
245
|
return `${/^\w+$/.test(p.name) ? p.name : `'${p.name}'`}${p.optional ? '?:' : ':'} ${type}${p.optional ? ' | undefined' : ''}`;
|
|
206
246
|
});
|
|
207
|
-
const route_type = `${s(route.id)}: { ${params.join('; ')} }`;
|
|
208
247
|
|
|
209
|
-
|
|
248
|
+
if (is_app_route(route)) {
|
|
249
|
+
dynamic_routes.push(`${s(route.id)}: { ${params.join('; ')} }`);
|
|
250
|
+
}
|
|
210
251
|
|
|
211
|
-
normalized_pathname = replace_required_params(replace_optional_params(pathname)).slice(1);
|
|
212
252
|
serialise = (p) => `\`${p}\` & {}`;
|
|
213
253
|
}
|
|
214
254
|
|
|
215
|
-
for (const
|
|
216
|
-
|
|
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
|
+
}
|
|
217
259
|
}
|
|
218
260
|
|
|
219
261
|
let layout_type = 'Record<string, never>';
|
|
@@ -230,7 +272,7 @@ function generate_app_types(manifest_data, config, dir) {
|
|
|
230
272
|
if (params.length > 0) layout_type = `{ ${params} }`;
|
|
231
273
|
}
|
|
232
274
|
|
|
233
|
-
layouts.push(`${s(route.id)}: ${layout_type}`);
|
|
275
|
+
if (route.layout) layouts.push(`${s(route.id)}: ${layout_type}`);
|
|
234
276
|
}
|
|
235
277
|
|
|
236
278
|
const assets = manifest_data.assets.map((asset) => s(asset.file));
|
|
@@ -238,7 +280,9 @@ function generate_app_types(manifest_data, config, dir) {
|
|
|
238
280
|
return [
|
|
239
281
|
'declare module "$app/types" {',
|
|
240
282
|
'\texport interface AppTypes {',
|
|
241
|
-
`\t\
|
|
283
|
+
`\t\tPageRouteId(): ${page_route_ids.join(' | ') || 'never'};`,
|
|
284
|
+
`\t\tEndpointRouteId(): ${endpoint_route_ids.join(' | ') || 'never'};`,
|
|
285
|
+
`\t\tRouteId(): ${app_route_ids.join(' | ') || 'never'};`,
|
|
242
286
|
`\t\tRouteParams(): {\n\t\t\t${dynamic_routes.join(';\n\t\t\t')}\n\t\t};`,
|
|
243
287
|
`\t\tLayoutParams(): {\n\t\t\t${layouts.join(';\n\t\t\t')}\n\t\t};`,
|
|
244
288
|
`\t\tPath(): ${Array.from(pathnames).join(' | ')};`,
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import process from 'node:process';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import ts from 'typescript';
|
|
6
5
|
import { styleText } from 'node:util';
|
|
7
6
|
import { write_if_changed } from '../utils.js';
|
|
8
7
|
import {
|
|
@@ -15,6 +14,14 @@ import {
|
|
|
15
14
|
validate_resolved_config
|
|
16
15
|
} from './utils.js';
|
|
17
16
|
|
|
17
|
+
/** @type {import('typescript')} */
|
|
18
|
+
let ts;
|
|
19
|
+
try {
|
|
20
|
+
ts = await import('typescript');
|
|
21
|
+
} catch {
|
|
22
|
+
// The user has not installed TypeScript.
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
/**
|
|
19
26
|
* Generates the tsconfig that the user's tsconfig inherits from.
|
|
20
27
|
* @param {import('types').ValidatedKitConfig} kit
|
|
@@ -78,13 +85,19 @@ export function write_tsconfig(kit, root) {
|
|
|
78
85
|
* @param {ValidatedKitConfig['typescript']['config']} [transform] TODO get rid of this
|
|
79
86
|
*/
|
|
80
87
|
function write_parent_tsconfig(root, dir, id, config, example, transform) {
|
|
81
|
-
|
|
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`);
|
|
82
90
|
|
|
83
91
|
let normalized = normalize_config(out_file, config);
|
|
84
92
|
normalized = transform?.(normalized) ?? normalized;
|
|
85
93
|
|
|
86
94
|
write_if_changed(out_file, JSON.stringify(normalized, null, '\t'));
|
|
87
95
|
|
|
96
|
+
if (!ts) {
|
|
97
|
+
// The user has not installed TypeScript. Skip validation of config.
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
88
101
|
const user_config = load_user_tsconfig(dir);
|
|
89
102
|
|
|
90
103
|
if (user_config && modified_since_last_check(user_config.file)) {
|
|
@@ -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
|
-
|
|
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)
|
package/src/exports/index.js
CHANGED
|
@@ -27,10 +27,10 @@ export { defineParams } from './params.js';
|
|
|
27
27
|
* return an error response without invoking `handleError`.
|
|
28
28
|
* Make sure you're not catching the thrown error, which would prevent SvelteKit from handling it.
|
|
29
29
|
* @param {number} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
|
|
30
|
-
* @param {
|
|
30
|
+
* @param {string} [message] The error message.
|
|
31
31
|
* @overload
|
|
32
|
-
* @param {number} status
|
|
33
|
-
* @param {
|
|
32
|
+
* @param {{ status: number; message: string } extends App.Error ? number : never} status
|
|
33
|
+
* @param {{ status: number; message: string } extends App.Error ? string : never} [message]
|
|
34
34
|
* @return {never}
|
|
35
35
|
* @throws {import('./public.js').HttpError} This error instructs SvelteKit to initiate HTTP error handling.
|
|
36
36
|
* @throws {Error} If the provided status is invalid (not between 400 and 599).
|
|
@@ -41,10 +41,12 @@ export { defineParams } from './params.js';
|
|
|
41
41
|
* return an error response without invoking `handleError`.
|
|
42
42
|
* Make sure you're not catching the thrown error, which would prevent SvelteKit from handling it.
|
|
43
43
|
* @param {number} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
|
|
44
|
-
* @param {
|
|
44
|
+
* @param {string} message The error message.
|
|
45
|
+
* @param {{ status: number; message: string } extends App.Error ? never : Omit<App.Error, 'status' | 'message'>} properties Additional properties of the App.Error type.
|
|
45
46
|
* @overload
|
|
46
47
|
* @param {number} status
|
|
47
|
-
* @param {
|
|
48
|
+
* @param {string} message
|
|
49
|
+
* @param {{ status: number; message: string } extends App.Error ? never : Omit<App.Error, 'status' | 'message'>} properties
|
|
48
50
|
* @return {never}
|
|
49
51
|
* @throws {import('./public.js').HttpError} This error instructs SvelteKit to initiate HTTP error handling.
|
|
50
52
|
* @throws {Error} If the provided status is invalid (not between 400 and 599).
|
|
@@ -54,13 +56,12 @@ export { defineParams } from './params.js';
|
|
|
54
56
|
* When called during request handling, this will cause SvelteKit to
|
|
55
57
|
* return an error response without invoking `handleError`.
|
|
56
58
|
* Make sure you're not catching the thrown error, which would prevent SvelteKit from handling it.
|
|
59
|
+
* @deprecated Passing an `App.Error` body as the second argument is deprecated — pass the `message` as the second argument, and any additional properties as the third
|
|
57
60
|
* @param {number} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
|
|
58
|
-
* @param {
|
|
59
|
-
* @param {{ status: number; message: string } extends App.Error ? never : Omit<App.Error, 'status' | 'message'>} properties Additional properties of the App.Error type.
|
|
61
|
+
* @param {Omit<App.Error, 'status'> & { status?: App.Error['status'] }} body An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
|
|
60
62
|
* @overload
|
|
61
63
|
* @param {number} status
|
|
62
|
-
* @param {
|
|
63
|
-
* @param {{ status: number; message: string } extends App.Error ? never : Omit<App.Error, 'status' | 'message'>} properties
|
|
64
|
+
* @param {Omit<App.Error, 'status'> & { status?: App.Error['status'] }} properties
|
|
64
65
|
* @return {never}
|
|
65
66
|
* @throws {import('./public.js').HttpError} This error instructs SvelteKit to initiate HTTP error handling.
|
|
66
67
|
* @throws {Error} If the provided status is invalid (not between 400 and 599).
|
|
@@ -71,18 +72,28 @@ export { defineParams } from './params.js';
|
|
|
71
72
|
* return an error response without invoking `handleError`.
|
|
72
73
|
* Make sure you're not catching the thrown error, which would prevent SvelteKit from handling it.
|
|
73
74
|
* @param {any} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
|
|
74
|
-
* @param {any} [
|
|
75
|
+
* @param {any} [message] A string, or (deprecated) a partial App.Error object
|
|
75
76
|
* @param {any} [properties] Additional properties of the App.Error type when passing a string message.
|
|
76
77
|
* @return {never}
|
|
77
78
|
* @throws {import('./public.js').HttpError} This error instructs SvelteKit to initiate HTTP error handling.
|
|
78
79
|
* @throws {Error} If the provided status is invalid (not between 400 and 599).
|
|
79
80
|
*/
|
|
80
|
-
export function error(status,
|
|
81
|
+
export function error(status, message, properties) {
|
|
81
82
|
if ((!BROWSER || DEV) && (isNaN(status) || status < 400 || status > 599)) {
|
|
82
83
|
throw new Error(`HTTP error status codes must be between 400 and 599 — ${status} is invalid`);
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
if (message !== undefined && typeof message !== 'string') {
|
|
87
|
+
if (DEV) {
|
|
88
|
+
console.warn(
|
|
89
|
+
'Passing an `App.Error` body as the second argument is deprecated — pass the `message` as the second argument, and any additional properties as the third'
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
({ message, ...properties } = message);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
throw new HttpError({ ...properties, status, message: message ?? `Error: ${status}` });
|
|
86
97
|
}
|
|
87
98
|
|
|
88
99
|
/**
|
|
@@ -2,19 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
export class HttpError {
|
|
4
4
|
/**
|
|
5
|
-
* @param {
|
|
6
|
-
* @param {Omit<App.Error, 'status'> | string | undefined} body
|
|
7
|
-
* @param {Omit<App.Error, 'status' | 'message'>} [properties]
|
|
5
|
+
* @param {App.Error} error
|
|
8
6
|
*/
|
|
9
|
-
constructor(
|
|
10
|
-
this.status = status;
|
|
11
|
-
|
|
12
|
-
this.body = { ...properties, message: body, status };
|
|
13
|
-
} else if (body) {
|
|
14
|
-
this.body = { ...body, status };
|
|
15
|
-
} else {
|
|
16
|
-
this.body = { message: `Error: ${status}`, status };
|
|
17
|
-
}
|
|
7
|
+
constructor(error) {
|
|
8
|
+
this.status = error.status;
|
|
9
|
+
this.body = error;
|
|
18
10
|
}
|
|
19
11
|
|
|
20
12
|
toString() {
|
|
@@ -137,14 +137,9 @@ export function getRequest({ request, base, bodySizeLimit }) {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
const controller = new AbortController();
|
|
140
|
-
|
|
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 (
|
|
142
|
+
if (request.readableAborted) {
|
|
148
143
|
controller.abort();
|
|
149
144
|
}
|
|
150
145
|
});
|