@sveltejs/kit 2.54.0 → 2.55.0
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
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { GENERATED_COMMENT } from '../../constants.js';
|
|
3
|
+
import { posixify } from '../../utils/filesystem.js';
|
|
3
4
|
import { write_if_changed } from './utils.js';
|
|
4
5
|
import { s } from '../../utils/misc.js';
|
|
5
6
|
import { get_route_segments } from '../../utils/routing.js';
|
|
@@ -24,25 +25,25 @@ function get_pathnames_for_trailing_slash(pathname, route) {
|
|
|
24
25
|
return [pathname];
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
/** @type {({ trailingSlash?: import('types').TrailingSlash } | null)[]} */
|
|
28
|
-
const routes = [];
|
|
29
|
-
|
|
30
|
-
if (route.leaf) routes.push(route.leaf.page_options ?? null);
|
|
31
|
-
if (route.endpoint) routes.push(route.endpoint.page_options);
|
|
32
|
-
|
|
33
28
|
/** @type {Set<string>} */
|
|
34
29
|
const pathnames = new Set();
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
/**
|
|
32
|
+
* @param {{ trailingSlash?: import('types').TrailingSlash } | null | undefined} page_options
|
|
33
|
+
*/
|
|
34
|
+
const add_pathnames = (page_options) => {
|
|
35
|
+
if (page_options === null || page_options?.trailingSlash === 'ignore') {
|
|
38
36
|
pathnames.add(pathname);
|
|
39
37
|
pathnames.add(pathname + '/');
|
|
40
|
-
} else if (page_options
|
|
38
|
+
} else if (page_options?.trailingSlash === 'always') {
|
|
41
39
|
pathnames.add(pathname + '/');
|
|
42
40
|
} else {
|
|
43
41
|
pathnames.add(pathname);
|
|
44
42
|
}
|
|
45
|
-
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
if (route.leaf) add_pathnames(route.leaf.page_options ?? null);
|
|
46
|
+
if (route.endpoint) add_pathnames(route.endpoint.page_options);
|
|
46
47
|
|
|
47
48
|
return Array.from(pathnames);
|
|
48
49
|
}
|
|
@@ -81,8 +82,66 @@ export {};
|
|
|
81
82
|
/**
|
|
82
83
|
* Generate app types interface extension
|
|
83
84
|
* @param {import('types').ManifestData} manifest_data
|
|
85
|
+
* @param {import('types').ValidatedKitConfig} config
|
|
84
86
|
*/
|
|
85
|
-
function generate_app_types(manifest_data) {
|
|
87
|
+
function generate_app_types(manifest_data, config) {
|
|
88
|
+
/** @param {string} matcher */
|
|
89
|
+
const path_to_matcher = (matcher) =>
|
|
90
|
+
posixify(path.relative(config.outDir, path.join(config.files.params, matcher + '.js')));
|
|
91
|
+
|
|
92
|
+
/** @type {Map<string, string>} */
|
|
93
|
+
const matcher_types = new Map();
|
|
94
|
+
|
|
95
|
+
/** @param {string | undefined} matcher */
|
|
96
|
+
const get_matcher_type = (matcher) => {
|
|
97
|
+
if (!matcher) return 'string';
|
|
98
|
+
|
|
99
|
+
let type = matcher_types.get(matcher);
|
|
100
|
+
if (!type) {
|
|
101
|
+
type = `MatcherParam<typeof import('${path_to_matcher(matcher)}').match>`;
|
|
102
|
+
matcher_types.set(matcher, type);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return type;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/** @param {Set<string> | null} matchers */
|
|
109
|
+
const get_matchers_type = (matchers) => {
|
|
110
|
+
if (matchers === null) return 'string';
|
|
111
|
+
|
|
112
|
+
return Array.from(matchers)
|
|
113
|
+
.map((matcher) => get_matcher_type(matcher))
|
|
114
|
+
.join(' | ');
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/** @type {Set<string>} */
|
|
118
|
+
const route_ids = new Set(manifest_data.routes.map((route) => route.id));
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {string} id
|
|
122
|
+
* @returns {string[]}
|
|
123
|
+
*/
|
|
124
|
+
const get_ancestor_route_ids = (id) => {
|
|
125
|
+
/** @type {string[]} */
|
|
126
|
+
const ancestors = [];
|
|
127
|
+
|
|
128
|
+
if (route_ids.has('/')) {
|
|
129
|
+
ancestors.push('/');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let current = '';
|
|
133
|
+
for (const segment of id.slice(1).split('/')) {
|
|
134
|
+
if (!segment) continue;
|
|
135
|
+
|
|
136
|
+
current += '/' + segment;
|
|
137
|
+
if (route_ids.has(current)) {
|
|
138
|
+
ancestors.push(current);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return ancestors;
|
|
143
|
+
};
|
|
144
|
+
|
|
86
145
|
/** @type {Set<string>} */
|
|
87
146
|
const pathnames = new Set();
|
|
88
147
|
|
|
@@ -92,49 +151,96 @@ function generate_app_types(manifest_data) {
|
|
|
92
151
|
/** @type {string[]} */
|
|
93
152
|
const layouts = [];
|
|
94
153
|
|
|
154
|
+
/** @type {Map<string, Map<string, { optional: boolean, matchers: Set<string> | null }>>} */
|
|
155
|
+
const layout_params_by_route = new Map(
|
|
156
|
+
manifest_data.routes.map((route) => [
|
|
157
|
+
route.id,
|
|
158
|
+
new Map(
|
|
159
|
+
route.params.map((p) => [
|
|
160
|
+
p.name,
|
|
161
|
+
{ optional: p.optional, matchers: p.matcher ? new Set([p.matcher]) : null }
|
|
162
|
+
])
|
|
163
|
+
)
|
|
164
|
+
])
|
|
165
|
+
);
|
|
166
|
+
|
|
95
167
|
for (const route of manifest_data.routes) {
|
|
168
|
+
const ancestors = get_ancestor_route_ids(route.id);
|
|
169
|
+
|
|
170
|
+
for (const ancestor_id of ancestors) {
|
|
171
|
+
const ancestor_params = layout_params_by_route.get(ancestor_id);
|
|
172
|
+
if (!ancestor_params) continue;
|
|
173
|
+
|
|
174
|
+
for (const p of route.params) {
|
|
175
|
+
const matcher = p.matcher ?? null;
|
|
176
|
+
const entry = ancestor_params.get(p.name);
|
|
177
|
+
if (!entry) {
|
|
178
|
+
ancestor_params.set(p.name, {
|
|
179
|
+
optional: true,
|
|
180
|
+
matchers: matcher === null ? null : new Set([matcher])
|
|
181
|
+
});
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (entry.matchers === null) continue;
|
|
186
|
+
|
|
187
|
+
if (matcher === null) {
|
|
188
|
+
entry.matchers = null;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
entry.matchers.add(matcher);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
for (const route of manifest_data.routes) {
|
|
198
|
+
const pathname = remove_group_segments(route.id);
|
|
199
|
+
let normalized_pathname = pathname;
|
|
200
|
+
|
|
201
|
+
/** @type {(path: string) => string} */
|
|
202
|
+
let serialise = s;
|
|
203
|
+
|
|
96
204
|
if (route.params.length > 0) {
|
|
97
|
-
const params = route.params.map((p) =>
|
|
205
|
+
const params = route.params.map((p) => {
|
|
206
|
+
const type = get_matcher_type(p.matcher);
|
|
207
|
+
return `${p.name}${p.optional ? '?:' : ':'} ${type}`;
|
|
208
|
+
});
|
|
98
209
|
const route_type = `${s(route.id)}: { ${params.join('; ')} }`;
|
|
99
210
|
|
|
100
211
|
dynamic_routes.push(route_type);
|
|
101
212
|
|
|
102
|
-
|
|
103
|
-
|
|
213
|
+
normalized_pathname = replace_required_params(replace_optional_params(pathname));
|
|
214
|
+
serialise = (p) => `\`${p}\` & {}`;
|
|
215
|
+
}
|
|
104
216
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
} else {
|
|
109
|
-
const pathname = remove_group_segments(route.id);
|
|
110
|
-
for (const p of get_pathnames_for_trailing_slash(pathname, route)) {
|
|
111
|
-
pathnames.add(s(p));
|
|
112
|
-
}
|
|
217
|
+
for (const p of get_pathnames_for_trailing_slash(normalized_pathname, route)) {
|
|
218
|
+
pathnames.add(serialise(p));
|
|
113
219
|
}
|
|
114
220
|
|
|
115
|
-
|
|
116
|
-
const child_params = new Map(route.params.map((p) => [p.name, p.optional]));
|
|
221
|
+
let layout_type = 'Record<string, never>';
|
|
117
222
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
223
|
+
const layout_params = layout_params_by_route.get(route.id);
|
|
224
|
+
if (layout_params) {
|
|
225
|
+
const params = Array.from(layout_params)
|
|
226
|
+
.map(([name, { optional, matchers }]) => {
|
|
227
|
+
const type = get_matchers_type(matchers);
|
|
228
|
+
return `${name}${optional ? '?:' : ':'} ${type}`;
|
|
229
|
+
})
|
|
230
|
+
.join('; ');
|
|
125
231
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
.join('; ');
|
|
232
|
+
if (params.length > 0) layout_type = `{ ${params} }`;
|
|
233
|
+
}
|
|
129
234
|
|
|
130
|
-
|
|
131
|
-
layouts.push(layout_type);
|
|
235
|
+
layouts.push(`${s(route.id)}: ${layout_type}`);
|
|
132
236
|
}
|
|
133
237
|
|
|
134
238
|
const assets = manifest_data.assets.map((asset) => s('/' + asset.file));
|
|
135
239
|
|
|
136
240
|
return [
|
|
137
241
|
'declare module "$app/types" {',
|
|
242
|
+
'\ttype MatcherParam<M> = M extends (param : string) => param is (infer U extends string) ? U : string;',
|
|
243
|
+
'',
|
|
138
244
|
'\texport interface AppTypes {',
|
|
139
245
|
`\t\tRouteId(): ${manifest_data.routes.map((r) => s(r.id)).join(' | ')};`,
|
|
140
246
|
`\t\tRouteParams(): {\n\t\t\t${dynamic_routes.join(';\n\t\t\t')}\n\t\t};`,
|
|
@@ -153,7 +259,7 @@ function generate_app_types(manifest_data) {
|
|
|
153
259
|
* @param {import('types').ManifestData} manifest_data
|
|
154
260
|
*/
|
|
155
261
|
export function write_non_ambient(config, manifest_data) {
|
|
156
|
-
const app_types = generate_app_types(manifest_data);
|
|
262
|
+
const app_types = generate_app_types(manifest_data, config);
|
|
157
263
|
const content = [template, app_types].join('\n\n');
|
|
158
264
|
|
|
159
265
|
write_if_changed(path.join(config.outDir, 'non-ambient.d.ts'), content);
|
|
@@ -199,11 +199,7 @@ function update_types(config, routes, route, to_delete = new Set()) {
|
|
|
199
199
|
|
|
200
200
|
// returns the predicate of a matcher's type guard - or string if there is no type guard
|
|
201
201
|
declarations.push(
|
|
202
|
-
|
|
203
|
-
[
|
|
204
|
-
'// @ts-ignore',
|
|
205
|
-
'type MatcherParam<M> = M extends (param : string) => param is infer U ? U extends string ? U : string : string;'
|
|
206
|
-
].join('\n')
|
|
202
|
+
'type MatcherParam<M> = M extends (param : string) => param is (infer U extends string) ? U : string;'
|
|
207
203
|
);
|
|
208
204
|
|
|
209
205
|
declarations.push(
|
|
@@ -11,7 +11,7 @@ function context_dev(name) {
|
|
|
11
11
|
return context();
|
|
12
12
|
} catch {
|
|
13
13
|
throw new Error(
|
|
14
|
-
`Can only read '${name}' on the server during rendering (not in e.g. \`load\` functions), as it is bound to the current request via component context. This prevents state from leaking between users
|
|
14
|
+
`Can only read '${name}' on the server during rendering (not in e.g. \`load\` functions), as it is bound to the current request via component context. This prevents state from leaking between users. ` +
|
|
15
15
|
'For more information, see https://svelte.dev/docs/kit/state-management#avoid-shared-state-on-the-server'
|
|
16
16
|
);
|
|
17
17
|
}
|
package/src/version.js
CHANGED