@sveltejs/kit 1.0.0-next.556 → 1.0.0-next.559
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 +1 -1
- package/src/core/generate_manifest/index.js +3 -5
- package/src/core/sync/create_manifest_data/index.js +11 -7
- package/src/core/sync/write_types/index.js +4 -4
- package/src/exports/index.js +11 -1
- package/src/exports/vite/build/utils.js +0 -3
- package/src/exports/vite/dev/index.js +1 -3
- package/src/exports/vite/index.js +0 -3
- package/src/runtime/client/client.js +5 -5
- package/src/runtime/client/parse.js +2 -2
- package/src/runtime/client/start.js +1 -1
- package/src/runtime/control.js +0 -53
- package/src/runtime/server/cookie.js +12 -7
- package/src/runtime/server/index.js +1 -1
- package/src/utils/routing.js +80 -47
- package/types/internal.d.ts +10 -8
package/package.json
CHANGED
|
@@ -99,8 +99,8 @@ export function generate_manifest({ build_data, relative_path, routes, format =
|
|
|
99
99
|
],
|
|
100
100
|
routes: [
|
|
101
101
|
${routes.map(route => {
|
|
102
|
-
route.
|
|
103
|
-
if (
|
|
102
|
+
route.params.forEach(param => {
|
|
103
|
+
if (param.matcher) matchers.add(param.matcher);
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
if (!route.page && !route.endpoint) return;
|
|
@@ -108,9 +108,7 @@ export function generate_manifest({ build_data, relative_path, routes, format =
|
|
|
108
108
|
return `{
|
|
109
109
|
id: ${s(route.id)},
|
|
110
110
|
pattern: ${route.pattern},
|
|
111
|
-
|
|
112
|
-
types: ${s(route.types)},
|
|
113
|
-
optional: ${s(route.optional)},
|
|
111
|
+
params: ${s(route.params)},
|
|
114
112
|
page: ${route.page ? `{ layouts: ${get_nodes(route.page.layouts)}, errors: ${get_nodes(route.page.errors)}, leaf: ${reindexed.get(route.page.leaf)} }` : 'null'},
|
|
115
113
|
endpoint: ${route.endpoint ? loader(join_relative(relative_path, resolve_symlinks(build_data.server.vite_manifest, route.endpoint.file).chunk.file)) : 'null'}
|
|
116
114
|
}`;
|
|
@@ -24,6 +24,14 @@ export default function create_manifest_data({
|
|
|
24
24
|
const matchers = create_matchers(config, cwd);
|
|
25
25
|
const { nodes, routes } = create_routes_and_nodes(cwd, config, fallback);
|
|
26
26
|
|
|
27
|
+
for (const route of routes) {
|
|
28
|
+
for (const param of route.params) {
|
|
29
|
+
if (param.matcher && !matchers[param.matcher]) {
|
|
30
|
+
throw new Error(`No matcher found for parameter '${param.matcher}' in route ${route.id}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
return {
|
|
28
36
|
assets,
|
|
29
37
|
matchers,
|
|
@@ -153,7 +161,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
|
|
|
153
161
|
);
|
|
154
162
|
}
|
|
155
163
|
|
|
156
|
-
const { pattern,
|
|
164
|
+
const { pattern, params } = parse_route_id(id);
|
|
157
165
|
|
|
158
166
|
/** @type {import('types').RouteData} */
|
|
159
167
|
const route = {
|
|
@@ -162,9 +170,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
|
|
|
162
170
|
|
|
163
171
|
segment,
|
|
164
172
|
pattern,
|
|
165
|
-
|
|
166
|
-
types,
|
|
167
|
-
optional,
|
|
173
|
+
params,
|
|
168
174
|
|
|
169
175
|
layout: null,
|
|
170
176
|
error: null,
|
|
@@ -273,9 +279,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
|
|
|
273
279
|
id: '/',
|
|
274
280
|
segment: '',
|
|
275
281
|
pattern: /^$/,
|
|
276
|
-
|
|
277
|
-
types: [],
|
|
278
|
-
optional: [],
|
|
282
|
+
params: [],
|
|
279
283
|
parent: null,
|
|
280
284
|
layout: null,
|
|
281
285
|
error: null,
|
|
@@ -195,8 +195,8 @@ function update_types(config, routes, route, to_delete = new Set()) {
|
|
|
195
195
|
// Makes sure a type is "repackaged" and therefore more readable
|
|
196
196
|
declarations.push('type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;');
|
|
197
197
|
declarations.push(
|
|
198
|
-
`type RouteParams = { ${route.
|
|
199
|
-
.map((param
|
|
198
|
+
`type RouteParams = { ${route.params
|
|
199
|
+
.map((param) => `${param.name}${param.optional ? '?' : ''}: string`)
|
|
200
200
|
.join('; ')} }`
|
|
201
201
|
);
|
|
202
202
|
|
|
@@ -270,8 +270,8 @@ function update_types(config, routes, route, to_delete = new Set()) {
|
|
|
270
270
|
if (leaf) {
|
|
271
271
|
if (leaf.route.page) ids.push(`"${leaf.route.id}"`);
|
|
272
272
|
|
|
273
|
-
for (const
|
|
274
|
-
layout_params.add(name);
|
|
273
|
+
for (const param of leaf.route.params) {
|
|
274
|
+
layout_params.add(param.name);
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
ensureProxies(page, leaf.proxies);
|
package/src/exports/index.js
CHANGED
|
@@ -8,12 +8,22 @@ import { HttpError, Redirect, ValidationError } from '../runtime/control.js';
|
|
|
8
8
|
* @param {any} message
|
|
9
9
|
*/
|
|
10
10
|
export function error(status, message) {
|
|
11
|
+
if (
|
|
12
|
+
(!__SVELTEKIT_BROWSER__ || __SVELTEKIT_DEV__) &&
|
|
13
|
+
(isNaN(status) || status < 400 || status > 599)
|
|
14
|
+
) {
|
|
15
|
+
throw new Error(`HTTP error status codes must be between 400 and 599 — ${status} is invalid`);
|
|
16
|
+
}
|
|
17
|
+
|
|
11
18
|
return new HttpError(status, message);
|
|
12
19
|
}
|
|
13
20
|
|
|
14
21
|
/** @type {import('@sveltejs/kit').redirect} */
|
|
15
22
|
export function redirect(status, location) {
|
|
16
|
-
if (
|
|
23
|
+
if (
|
|
24
|
+
(!__SVELTEKIT_BROWSER__ || __SVELTEKIT_DEV__) &&
|
|
25
|
+
(isNaN(status) || status < 300 || status > 308)
|
|
26
|
+
) {
|
|
17
27
|
throw new Error('Invalid status code');
|
|
18
28
|
}
|
|
19
29
|
|
|
@@ -127,9 +127,6 @@ export function get_default_build_config({ config, input, ssr, outDir }) {
|
|
|
127
127
|
cssCodeSplit: true,
|
|
128
128
|
// don't use the default name to avoid collisions with 'static/manifest.json'
|
|
129
129
|
manifest: 'vite-manifest.json',
|
|
130
|
-
modulePreload: {
|
|
131
|
-
polyfill: false
|
|
132
|
-
},
|
|
133
130
|
outDir,
|
|
134
131
|
rollupOptions: {
|
|
135
132
|
input,
|
|
@@ -156,9 +156,7 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
156
156
|
return {
|
|
157
157
|
id: route.id,
|
|
158
158
|
pattern: route.pattern,
|
|
159
|
-
|
|
160
|
-
types: route.types,
|
|
161
|
-
optional: route.optional,
|
|
159
|
+
params: route.params,
|
|
162
160
|
page: route.page,
|
|
163
161
|
endpoint: endpoint
|
|
164
162
|
? async () => {
|
|
@@ -75,7 +75,7 @@ function check_for_removed_attributes() {
|
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* @param {{
|
|
78
|
-
* target:
|
|
78
|
+
* target: HTMLElement;
|
|
79
79
|
* base: string;
|
|
80
80
|
* }} opts
|
|
81
81
|
* @returns {import('./types').Client}
|
|
@@ -1385,12 +1385,12 @@ export function create_client({ target, base }) {
|
|
|
1385
1385
|
}, 20);
|
|
1386
1386
|
};
|
|
1387
1387
|
|
|
1388
|
-
addEventListener('touchstart', trigger_prefetch);
|
|
1389
|
-
addEventListener('mousemove', handle_mousemove);
|
|
1390
|
-
addEventListener('sveltekit:trigger_prefetch', trigger_prefetch);
|
|
1388
|
+
target.addEventListener('touchstart', trigger_prefetch);
|
|
1389
|
+
target.addEventListener('mousemove', handle_mousemove);
|
|
1390
|
+
target.addEventListener('sveltekit:trigger_prefetch', trigger_prefetch);
|
|
1391
1391
|
|
|
1392
1392
|
/** @param {MouseEvent} event */
|
|
1393
|
-
addEventListener('click', (event) => {
|
|
1393
|
+
target.addEventListener('click', (event) => {
|
|
1394
1394
|
// Adapted from https://github.com/visionmedia/page.js
|
|
1395
1395
|
// MIT license https://github.com/visionmedia/page.js#license
|
|
1396
1396
|
if (event.button || event.which !== 1) return;
|
|
@@ -11,14 +11,14 @@ export function parse(nodes, server_loads, dictionary, matchers) {
|
|
|
11
11
|
const layouts_with_server_load = new Set(server_loads);
|
|
12
12
|
|
|
13
13
|
return Object.entries(dictionary).map(([id, [leaf, layouts, errors]]) => {
|
|
14
|
-
const { pattern,
|
|
14
|
+
const { pattern, params } = parse_route_id(id);
|
|
15
15
|
|
|
16
16
|
const route = {
|
|
17
17
|
id,
|
|
18
18
|
/** @param {string} path */
|
|
19
19
|
exec: (path) => {
|
|
20
20
|
const match = pattern.exec(path);
|
|
21
|
-
if (match) return exec(match,
|
|
21
|
+
if (match) return exec(match, params, matchers);
|
|
22
22
|
},
|
|
23
23
|
errors: [1, ...(errors || [])].map((n) => nodes[n]),
|
|
24
24
|
layouts: [0, ...(layouts || [])].map(create_layout_loader),
|
package/src/runtime/control.js
CHANGED
|
@@ -43,56 +43,3 @@ export class ValidationError {
|
|
|
43
43
|
this.data = data;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Creates an `HttpError` object with an HTTP status code and an optional message.
|
|
49
|
-
* This object, if thrown during request handling, will cause SvelteKit to
|
|
50
|
-
* return an error response without invoking `handleError`
|
|
51
|
-
* @param {number} status
|
|
52
|
-
* @param {string | undefined} [message]
|
|
53
|
-
*/
|
|
54
|
-
export function error(status, message) {
|
|
55
|
-
return new HttpError(status, message);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Creates a `Redirect` object. If thrown during request handling, SvelteKit will
|
|
60
|
-
* return a redirect response.
|
|
61
|
-
* @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308} status
|
|
62
|
-
* @param {string} location
|
|
63
|
-
*/
|
|
64
|
-
export function redirect(status, location) {
|
|
65
|
-
if (isNaN(status) || status < 300 || status > 308) {
|
|
66
|
-
throw new Error('Invalid status code');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return new Redirect(status, location);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Generates a JSON `Response` object from the supplied data.
|
|
74
|
-
* @param {any} data
|
|
75
|
-
* @param {ResponseInit} [init]
|
|
76
|
-
*/
|
|
77
|
-
export function json(data, init) {
|
|
78
|
-
// TODO deprecate this in favour of `Response.json` when it's
|
|
79
|
-
// more widely supported
|
|
80
|
-
const headers = new Headers(init?.headers);
|
|
81
|
-
if (!headers.has('content-type')) {
|
|
82
|
-
headers.set('content-type', 'application/json');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return new Response(JSON.stringify(data), {
|
|
86
|
-
...init,
|
|
87
|
-
headers
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Generates a `ValidationError` object.
|
|
93
|
-
* @param {number} status
|
|
94
|
-
* @param {Record<string, any> | undefined} [data]
|
|
95
|
-
*/
|
|
96
|
-
export function invalid(status, data) {
|
|
97
|
-
return new ValidationError(status, data);
|
|
98
|
-
}
|
|
@@ -8,6 +8,10 @@ import { has_data_suffix, normalize_path, strip_data_suffix } from '../../utils/
|
|
|
8
8
|
* @type {Record<string, Set<string>>} */
|
|
9
9
|
const cookie_paths = {};
|
|
10
10
|
|
|
11
|
+
// default encoding functions for header cookie values
|
|
12
|
+
const encode = encodeURIComponent;
|
|
13
|
+
const decode = decodeURIComponent;
|
|
14
|
+
|
|
11
15
|
/**
|
|
12
16
|
* @param {Request} request
|
|
13
17
|
* @param {URL} url
|
|
@@ -16,7 +20,7 @@ const cookie_paths = {};
|
|
|
16
20
|
*/
|
|
17
21
|
export function get_cookies(request, url, dev, trailing_slash) {
|
|
18
22
|
const header = request.headers.get('cookie') ?? '';
|
|
19
|
-
const initial_cookies = parse(header);
|
|
23
|
+
const initial_cookies = parse(header, { decode });
|
|
20
24
|
|
|
21
25
|
const normalized_url = normalize_path(
|
|
22
26
|
// Remove suffix: 'foo/__data.json' would mean the cookie path is '/foo',
|
|
@@ -76,8 +80,8 @@ export function get_cookies(request, url, dev, trailing_slash) {
|
|
|
76
80
|
return c.value;
|
|
77
81
|
}
|
|
78
82
|
|
|
79
|
-
const
|
|
80
|
-
const req_cookies = parse(header, { decode });
|
|
83
|
+
const decoder = opts?.decode || decode;
|
|
84
|
+
const req_cookies = parse(header, { decode: decoder });
|
|
81
85
|
const cookie = req_cookies[name]; // the decoded string or undefined
|
|
82
86
|
|
|
83
87
|
if (!dev || cookie) {
|
|
@@ -166,7 +170,7 @@ export function get_cookies(request, url, dev, trailing_slash) {
|
|
|
166
170
|
|
|
167
171
|
// cookies sent by the user agent have lowest precedence
|
|
168
172
|
for (const name in initial_cookies) {
|
|
169
|
-
combined_cookies[name] = initial_cookies[name];
|
|
173
|
+
combined_cookies[name] = encode(initial_cookies[name]);
|
|
170
174
|
}
|
|
171
175
|
|
|
172
176
|
// cookies previous set during this event with cookies.set have higher precedence
|
|
@@ -175,14 +179,15 @@ export function get_cookies(request, url, dev, trailing_slash) {
|
|
|
175
179
|
if (!domain_matches(destination.hostname, cookie.options.domain)) continue;
|
|
176
180
|
if (!path_matches(destination.pathname, cookie.options.path)) continue;
|
|
177
181
|
|
|
178
|
-
|
|
182
|
+
const encoder = cookie.options.encode || encode;
|
|
183
|
+
combined_cookies[cookie.name] = encoder(cookie.value);
|
|
179
184
|
}
|
|
180
185
|
|
|
181
186
|
// explicit header has highest precedence
|
|
182
187
|
if (header) {
|
|
183
|
-
const parsed = parse(header);
|
|
188
|
+
const parsed = parse(header, { decode });
|
|
184
189
|
for (const name in parsed) {
|
|
185
|
-
combined_cookies[name] = parsed[name];
|
|
190
|
+
combined_cookies[name] = encode(parsed[name]);
|
|
186
191
|
}
|
|
187
192
|
}
|
|
188
193
|
|
|
@@ -77,7 +77,7 @@ export async function respond(request, options, state) {
|
|
|
77
77
|
const match = candidate.pattern.exec(decoded);
|
|
78
78
|
if (!match) continue;
|
|
79
79
|
|
|
80
|
-
const matched = exec(match, candidate, matchers);
|
|
80
|
+
const matched = exec(match, candidate.params, matchers);
|
|
81
81
|
if (matched) {
|
|
82
82
|
route = candidate;
|
|
83
83
|
params = decode_params(matched);
|
package/src/utils/routing.js
CHANGED
|
@@ -5,44 +5,40 @@ const param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;
|
|
|
5
5
|
* @param {string} id
|
|
6
6
|
*/
|
|
7
7
|
export function parse_route_id(id) {
|
|
8
|
-
/** @type {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
/** @type {string[]} */
|
|
12
|
-
const types = [];
|
|
13
|
-
|
|
14
|
-
/** @type {boolean[]} */
|
|
15
|
-
const optional = [];
|
|
16
|
-
|
|
17
|
-
// `/foo` should get an optional trailing slash, `/foo.json` should not
|
|
18
|
-
// const add_trailing_slash = !/\.[a-z]+$/.test(key);
|
|
19
|
-
let add_trailing_slash = true;
|
|
8
|
+
/** @type {import('types').RouteParam[]} */
|
|
9
|
+
const params = [];
|
|
20
10
|
|
|
21
11
|
const pattern =
|
|
22
12
|
id === '/'
|
|
23
13
|
? /^\/$/
|
|
24
14
|
: new RegExp(
|
|
25
15
|
`^${get_route_segments(id)
|
|
26
|
-
.map((segment
|
|
16
|
+
.map((segment) => {
|
|
27
17
|
// special case — /[...rest]/ could contain zero segments
|
|
28
18
|
const rest_match = /^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(segment);
|
|
29
19
|
if (rest_match) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
params.push({
|
|
21
|
+
name: rest_match[1],
|
|
22
|
+
matcher: rest_match[2],
|
|
23
|
+
optional: false,
|
|
24
|
+
rest: true,
|
|
25
|
+
chained: true
|
|
26
|
+
});
|
|
33
27
|
return '(?:/(.*))?';
|
|
34
28
|
}
|
|
35
29
|
// special case — /[[optional]]/ could contain zero segments
|
|
36
30
|
const optional_match = /^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(segment);
|
|
37
31
|
if (optional_match) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
params.push({
|
|
33
|
+
name: optional_match[1],
|
|
34
|
+
matcher: optional_match[2],
|
|
35
|
+
optional: true,
|
|
36
|
+
rest: false,
|
|
37
|
+
chained: true
|
|
38
|
+
});
|
|
41
39
|
return '(?:/([^/]+))?';
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
const is_last = i === segments.length - 1;
|
|
45
|
-
|
|
46
42
|
if (!segment) {
|
|
47
43
|
return;
|
|
48
44
|
}
|
|
@@ -73,29 +69,31 @@ export function parse_route_id(id) {
|
|
|
73
69
|
);
|
|
74
70
|
}
|
|
75
71
|
|
|
76
|
-
const [, is_optional, is_rest, name,
|
|
72
|
+
const [, is_optional, is_rest, name, matcher] = match;
|
|
77
73
|
// It's assumed that the following invalid route id cases are already checked
|
|
78
74
|
// - unbalanced brackets
|
|
79
75
|
// - optional param following rest param
|
|
80
76
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
params.push({
|
|
78
|
+
name,
|
|
79
|
+
matcher,
|
|
80
|
+
optional: !!is_optional,
|
|
81
|
+
rest: !!is_rest,
|
|
82
|
+
chained: is_rest ? i === 1 && parts[0] === '' : false
|
|
83
|
+
});
|
|
84
84
|
return is_rest ? '(.*?)' : is_optional ? '([^/]*)?' : '([^/]+?)';
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
if (is_last && content.includes('.')) add_trailing_slash = false;
|
|
88
|
-
|
|
89
87
|
return escape(content);
|
|
90
88
|
})
|
|
91
89
|
.join('');
|
|
92
90
|
|
|
93
91
|
return '/' + result;
|
|
94
92
|
})
|
|
95
|
-
.join('')}
|
|
93
|
+
.join('')}/?$`
|
|
96
94
|
);
|
|
97
95
|
|
|
98
|
-
return { pattern,
|
|
96
|
+
return { pattern, params };
|
|
99
97
|
}
|
|
100
98
|
|
|
101
99
|
/**
|
|
@@ -119,35 +117,70 @@ export function get_route_segments(route) {
|
|
|
119
117
|
|
|
120
118
|
/**
|
|
121
119
|
* @param {RegExpMatchArray} match
|
|
122
|
-
* @param {
|
|
123
|
-
* names: string[];
|
|
124
|
-
* types: string[];
|
|
125
|
-
* optional: boolean[];
|
|
126
|
-
* }} candidate
|
|
120
|
+
* @param {import('types').RouteParam[]} params
|
|
127
121
|
* @param {Record<string, import('types').ParamMatcher>} matchers
|
|
128
122
|
*/
|
|
129
|
-
export function exec(match,
|
|
123
|
+
export function exec(match, params, matchers) {
|
|
130
124
|
/** @type {Record<string, string>} */
|
|
131
|
-
const
|
|
125
|
+
const result = {};
|
|
126
|
+
|
|
127
|
+
const values = match.slice(1);
|
|
132
128
|
|
|
133
|
-
|
|
134
|
-
const name = names[i];
|
|
135
|
-
const type = types[i];
|
|
136
|
-
let value = match[i + 1];
|
|
129
|
+
let buffered = '';
|
|
137
130
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
131
|
+
for (let i = 0; i < params.length; i += 1) {
|
|
132
|
+
const param = params[i];
|
|
133
|
+
let value = values[i];
|
|
134
|
+
|
|
135
|
+
if (param.chained && param.rest && buffered) {
|
|
136
|
+
// in the `[[lang=lang]]/[...rest]` case, if `lang` didn't
|
|
137
|
+
// match, we roll it over into the rest value
|
|
138
|
+
value = value ? buffered + '/' + value : buffered;
|
|
139
|
+
}
|
|
142
140
|
|
|
143
|
-
|
|
141
|
+
buffered = '';
|
|
142
|
+
|
|
143
|
+
if (value === undefined) {
|
|
144
|
+
// if `value` is undefined, it means this is
|
|
145
|
+
// an optional or rest parameter
|
|
146
|
+
if (param.rest) result[param.name] = '';
|
|
147
|
+
} else {
|
|
148
|
+
if (param.matcher && !matchers[param.matcher](value)) {
|
|
149
|
+
// in the `/[[a=b]]/[[c=d]]` case, if the value didn't satisfy the `b` matcher,
|
|
150
|
+
// try again with the next segment by shifting values rightwards
|
|
151
|
+
if (param.optional && param.chained) {
|
|
152
|
+
// @ts-expect-error TypeScript is... wrong
|
|
153
|
+
let j = values.indexOf(undefined, i);
|
|
154
|
+
|
|
155
|
+
if (j === -1) {
|
|
156
|
+
// we can't shift values any further, so hang on to this value
|
|
157
|
+
// so it can be rolled into a subsequent `[...rest]` param
|
|
158
|
+
const next = params[i + 1];
|
|
159
|
+
if (next?.rest && next.chained) {
|
|
160
|
+
buffered = value;
|
|
161
|
+
} else {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
while (j >= i) {
|
|
167
|
+
values[j] = values[j - 1];
|
|
168
|
+
j -= 1;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// otherwise, if the matcher returns `false`, the route did not match
|
|
175
|
+
return;
|
|
144
176
|
}
|
|
145
177
|
|
|
146
|
-
|
|
178
|
+
result[param.name] = value;
|
|
147
179
|
}
|
|
148
180
|
}
|
|
149
181
|
|
|
150
|
-
return
|
|
182
|
+
if (buffered) return;
|
|
183
|
+
return result;
|
|
151
184
|
}
|
|
152
185
|
|
|
153
186
|
/** @param {string} str */
|
package/types/internal.d.ts
CHANGED
|
@@ -158,6 +158,14 @@ export interface Respond {
|
|
|
158
158
|
(request: Request, options: SSROptions, state: SSRState): Promise<Response>;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
export interface RouteParam {
|
|
162
|
+
name: string;
|
|
163
|
+
matcher: string;
|
|
164
|
+
optional: boolean;
|
|
165
|
+
rest: boolean;
|
|
166
|
+
chained: boolean;
|
|
167
|
+
}
|
|
168
|
+
|
|
161
169
|
/**
|
|
162
170
|
* Represents a route segment in the app. It can either be an intermediate node
|
|
163
171
|
* with only layout/error pages, or a leaf, at which point either `page` and `leaf`
|
|
@@ -169,9 +177,7 @@ export interface RouteData {
|
|
|
169
177
|
|
|
170
178
|
segment: string;
|
|
171
179
|
pattern: RegExp;
|
|
172
|
-
|
|
173
|
-
types: string[];
|
|
174
|
-
optional: boolean[];
|
|
180
|
+
params: RouteParam[];
|
|
175
181
|
|
|
176
182
|
layout: PageNode | null;
|
|
177
183
|
error: PageNode | null;
|
|
@@ -337,12 +343,8 @@ export type SSREndpoint = Partial<Record<HttpMethod, RequestHandler>> & {
|
|
|
337
343
|
export interface SSRRoute {
|
|
338
344
|
id: string;
|
|
339
345
|
pattern: RegExp;
|
|
340
|
-
|
|
341
|
-
types: string[];
|
|
342
|
-
optional: boolean[];
|
|
343
|
-
|
|
346
|
+
params: RouteParam[];
|
|
344
347
|
page: PageNodeIndexes | null;
|
|
345
|
-
|
|
346
348
|
endpoint: (() => Promise<SSREndpoint>) | null;
|
|
347
349
|
}
|
|
348
350
|
|