@sveltejs/kit 1.0.0-next.43 → 1.0.0-next.430
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/README.md +12 -9
- package/package.json +95 -63
- package/src/cli.js +112 -0
- package/src/core/adapt/builder.js +207 -0
- package/src/core/adapt/index.js +19 -0
- package/src/core/config/index.js +86 -0
- package/src/core/config/options.js +488 -0
- package/src/core/config/types.d.ts +1 -0
- package/src/core/constants.js +5 -0
- package/src/core/env.js +97 -0
- package/src/core/generate_manifest/index.js +99 -0
- package/src/core/prerender/crawl.js +194 -0
- package/src/core/prerender/prerender.js +378 -0
- package/src/core/prerender/queue.js +80 -0
- package/src/core/sync/create_manifest_data/index.js +506 -0
- package/src/core/sync/create_manifest_data/types.d.ts +40 -0
- package/src/core/sync/sync.js +59 -0
- package/src/core/sync/utils.js +44 -0
- package/src/core/sync/write_ambient.js +27 -0
- package/src/core/sync/write_client_manifest.js +82 -0
- package/src/core/sync/write_matchers.js +25 -0
- package/src/core/sync/write_root.js +91 -0
- package/src/core/sync/write_tsconfig.js +195 -0
- package/src/core/sync/write_types.js +775 -0
- package/src/core/utils.js +70 -0
- package/src/hooks.js +26 -0
- package/src/index/index.js +45 -0
- package/src/index/private.js +33 -0
- package/src/node/index.js +145 -0
- package/src/node/polyfills.js +40 -0
- package/src/runtime/app/env.js +11 -0
- package/src/runtime/app/navigation.js +22 -0
- package/src/runtime/app/paths.js +1 -0
- package/src/runtime/app/stores.js +102 -0
- package/src/runtime/client/ambient.d.ts +17 -0
- package/src/runtime/client/client.js +1289 -0
- package/src/runtime/client/fetcher.js +60 -0
- package/src/runtime/client/parse.js +36 -0
- package/src/runtime/client/singletons.js +21 -0
- package/src/runtime/client/start.js +46 -0
- package/src/runtime/client/types.d.ts +105 -0
- package/src/runtime/client/utils.js +113 -0
- package/src/runtime/components/error.svelte +16 -0
- package/{assets → src/runtime}/components/layout.svelte +0 -0
- package/src/runtime/env/dynamic/private.js +1 -0
- package/src/runtime/env/dynamic/public.js +1 -0
- package/src/runtime/env-private.js +7 -0
- package/src/runtime/env-public.js +7 -0
- package/src/runtime/env.js +6 -0
- package/src/runtime/hash.js +16 -0
- package/src/runtime/paths.js +11 -0
- package/src/runtime/server/endpoint.js +58 -0
- package/src/runtime/server/index.js +448 -0
- package/src/runtime/server/page/cookie.js +25 -0
- package/src/runtime/server/page/crypto.js +239 -0
- package/src/runtime/server/page/csp.js +249 -0
- package/src/runtime/server/page/fetch.js +266 -0
- package/src/runtime/server/page/index.js +416 -0
- package/src/runtime/server/page/load_data.js +135 -0
- package/src/runtime/server/page/render.js +362 -0
- package/src/runtime/server/page/respond_with_error.js +94 -0
- package/src/runtime/server/page/types.d.ts +44 -0
- package/src/runtime/server/utils.js +116 -0
- package/src/utils/error.js +22 -0
- package/src/utils/escape.js +104 -0
- package/src/utils/filesystem.js +108 -0
- package/src/utils/http.js +55 -0
- package/src/utils/misc.js +1 -0
- package/src/utils/routing.js +108 -0
- package/src/utils/url.js +97 -0
- package/src/vite/build/build_server.js +337 -0
- package/src/vite/build/build_service_worker.js +90 -0
- package/src/vite/build/utils.js +160 -0
- package/src/vite/dev/index.js +551 -0
- package/src/vite/index.js +574 -0
- package/src/vite/preview/index.js +186 -0
- package/src/vite/types.d.ts +3 -0
- package/src/vite/utils.js +345 -0
- package/svelte-kit.js +1 -1
- package/types/ambient.d.ts +357 -0
- package/types/index.d.ts +343 -0
- package/types/internal.d.ts +308 -0
- package/types/private.d.ts +209 -0
- package/CHANGELOG.md +0 -431
- package/assets/components/error.svelte +0 -13
- package/assets/runtime/app/env.js +0 -5
- package/assets/runtime/app/navigation.js +0 -41
- package/assets/runtime/app/paths.js +0 -1
- package/assets/runtime/app/stores.js +0 -93
- package/assets/runtime/chunks/utils.js +0 -19
- package/assets/runtime/internal/singletons.js +0 -23
- package/assets/runtime/internal/start.js +0 -770
- package/assets/runtime/paths.js +0 -12
- package/dist/.DS_Store +0 -0
- package/dist/chunks/index.js +0 -3521
- package/dist/chunks/index2.js +0 -587
- package/dist/chunks/index3.js +0 -246
- package/dist/chunks/index4.js +0 -538
- package/dist/chunks/index5.js +0 -761
- package/dist/chunks/index6.js +0 -322
- package/dist/chunks/standard.js +0 -99
- package/dist/chunks/utils.js +0 -83
- package/dist/cli.js +0 -546
- package/dist/ssr.js +0 -2581
|
@@ -0,0 +1,775 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import MagicString from 'magic-string';
|
|
4
|
+
import { posixify, rimraf } from '../../utils/filesystem.js';
|
|
5
|
+
import { parse_route_id } from '../../utils/routing.js';
|
|
6
|
+
import { remove_from_previous, write_if_changed } from './utils.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef { import('types').PageNode & {
|
|
10
|
+
* parent?: {
|
|
11
|
+
* key: string;
|
|
12
|
+
* name: string;
|
|
13
|
+
* folder_depth_diff: number;
|
|
14
|
+
* }
|
|
15
|
+
* } } Node
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {{
|
|
20
|
+
* leaf?: Node;
|
|
21
|
+
* default_layout?: Node;
|
|
22
|
+
* named_layouts: Map<string, Node>;
|
|
23
|
+
* endpoint?: string;
|
|
24
|
+
* }} NodeGroup
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {{
|
|
29
|
+
* modified: boolean;
|
|
30
|
+
* code: string;
|
|
31
|
+
* exports: any[];
|
|
32
|
+
* } | null} Proxy
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const cwd = process.cwd();
|
|
36
|
+
|
|
37
|
+
const shared_names = new Set(['load']);
|
|
38
|
+
const server_names = new Set(['load', 'POST', 'PUT', 'PATCH', 'DELETE']); // TODO replace with a single `action`
|
|
39
|
+
|
|
40
|
+
let first_run = true;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Creates types for the whole manifest
|
|
44
|
+
*
|
|
45
|
+
* @param {import('types').ValidatedConfig} config
|
|
46
|
+
* @param {import('types').ManifestData} manifest_data
|
|
47
|
+
*/
|
|
48
|
+
export async function write_types(config, manifest_data) {
|
|
49
|
+
/** @type {import('typescript') | undefined} */
|
|
50
|
+
let ts = undefined;
|
|
51
|
+
try {
|
|
52
|
+
ts = (await import('typescript')).default;
|
|
53
|
+
} catch (e) {
|
|
54
|
+
// No TypeScript installed - skip type generation
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const types_dir = `${config.kit.outDir}/types`;
|
|
59
|
+
|
|
60
|
+
if (first_run) {
|
|
61
|
+
rimraf(types_dir);
|
|
62
|
+
first_run = false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const routes_dir = posixify(path.relative('.', config.kit.files.routes));
|
|
66
|
+
const groups = get_groups(manifest_data, routes_dir);
|
|
67
|
+
|
|
68
|
+
let written_files = new Set();
|
|
69
|
+
// ...then, for each directory, write $types.d.ts
|
|
70
|
+
for (const [dir] of groups) {
|
|
71
|
+
const written = write_types_for_dir(config, manifest_data, routes_dir, dir, groups, ts);
|
|
72
|
+
written.forEach((w) => written_files.add(w));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Remove all files that were not updated, which means their original was removed
|
|
76
|
+
remove_from_previous((file) => {
|
|
77
|
+
const was_removed = file.startsWith(types_dir) && !written_files.has(file);
|
|
78
|
+
if (was_removed) {
|
|
79
|
+
rimraf(file);
|
|
80
|
+
}
|
|
81
|
+
return was_removed;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Creates types related to the given file. This should only be called
|
|
87
|
+
* if the file in question was edited, not if it was created/deleted/moved.
|
|
88
|
+
*
|
|
89
|
+
* @param {import('types').ValidatedConfig} config
|
|
90
|
+
* @param {import('types').ManifestData} manifest_data
|
|
91
|
+
* @param {string} file
|
|
92
|
+
*/
|
|
93
|
+
export async function write_type(config, manifest_data, file) {
|
|
94
|
+
if (!path.basename(file).startsWith('+')) {
|
|
95
|
+
// Not a route file
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** @type {import('typescript') | undefined} */
|
|
100
|
+
let ts = undefined;
|
|
101
|
+
try {
|
|
102
|
+
ts = (await import('typescript')).default;
|
|
103
|
+
} catch (e) {
|
|
104
|
+
// No TypeScript installed - skip type generation
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const routes_dir = posixify(path.relative('.', config.kit.files.routes));
|
|
109
|
+
const file_dir = posixify(path.dirname(file).slice(config.kit.files.routes.length + 1));
|
|
110
|
+
const groups = get_groups(manifest_data, routes_dir);
|
|
111
|
+
|
|
112
|
+
// We are only interested in the directory that contains the file
|
|
113
|
+
write_types_for_dir(config, manifest_data, routes_dir, file_dir, groups, ts);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {import('types').ManifestData} manifest_data
|
|
118
|
+
* @param {string} routes_dir
|
|
119
|
+
*/
|
|
120
|
+
function get_groups(manifest_data, routes_dir) {
|
|
121
|
+
/**
|
|
122
|
+
* A map of all directories : route files. We don't just use
|
|
123
|
+
* manifest_data.routes, because that will exclude +layout
|
|
124
|
+
* files that aren't accompanied by a +page
|
|
125
|
+
* @type {Map<string, NodeGroup>}
|
|
126
|
+
*/
|
|
127
|
+
const groups = new Map();
|
|
128
|
+
|
|
129
|
+
/** @param {string} dir */
|
|
130
|
+
function get_group(dir) {
|
|
131
|
+
let group = groups.get(dir);
|
|
132
|
+
if (!group) {
|
|
133
|
+
group = {
|
|
134
|
+
named_layouts: new Map()
|
|
135
|
+
};
|
|
136
|
+
groups.set(dir, group);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return group;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// first, sort nodes (necessary for finding the nearest layout more efficiently)...
|
|
143
|
+
const nodes = [...manifest_data.nodes].sort((n1, n2) => {
|
|
144
|
+
// Sort by path length first...
|
|
145
|
+
const path_length_diff =
|
|
146
|
+
/** @type {string} */ (n1.component ?? n1.shared ?? n1.server).split('/').length -
|
|
147
|
+
/** @type {string} */ (n2.component ?? n2.shared ?? n2.server).split('/').length;
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
path_length_diff ||
|
|
151
|
+
// ...on ties, sort named layouts first
|
|
152
|
+
(path.basename(n1.component || '').includes('-')
|
|
153
|
+
? -1
|
|
154
|
+
: path.basename(n2.component || '').includes('-')
|
|
155
|
+
? 1
|
|
156
|
+
: 0)
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// ...then, populate `directories` with +page/+layout files...
|
|
161
|
+
for (let i = 0; i < nodes.length; i += 1) {
|
|
162
|
+
/** @type {Node} */
|
|
163
|
+
const node = { ...nodes[i] }; // shallow copy so we don't mutate the original when setting parent
|
|
164
|
+
|
|
165
|
+
const file_path = /** @type {string} */ (node.component ?? node.shared ?? node.server);
|
|
166
|
+
// skip default layout/error
|
|
167
|
+
if (!file_path.startsWith(routes_dir)) continue;
|
|
168
|
+
|
|
169
|
+
const parts = file_path.split('/');
|
|
170
|
+
|
|
171
|
+
const file = /** @type {string} */ (parts.pop());
|
|
172
|
+
const dir = parts.join('/').slice(routes_dir.length + 1);
|
|
173
|
+
|
|
174
|
+
// error pages don't need types
|
|
175
|
+
if (!file || file.startsWith('+error')) continue;
|
|
176
|
+
|
|
177
|
+
const group = get_group(dir);
|
|
178
|
+
|
|
179
|
+
if (file.startsWith('+page')) {
|
|
180
|
+
group.leaf = node;
|
|
181
|
+
} else {
|
|
182
|
+
const match = /^\+layout(?:-([^@.]+))?/.exec(file);
|
|
183
|
+
|
|
184
|
+
// this shouldn't happen, but belt and braces. also keeps TS happy,
|
|
185
|
+
// and we live to keep TS happy
|
|
186
|
+
if (!match) throw new Error(`Unexpected route file: ${file}`);
|
|
187
|
+
|
|
188
|
+
if (match[1]) {
|
|
189
|
+
group.named_layouts.set(match[1], node);
|
|
190
|
+
} else {
|
|
191
|
+
group.default_layout = node;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
node.parent = find_nearest_layout(routes_dir, nodes, i);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ...then add +server.js files...
|
|
199
|
+
for (const route of manifest_data.routes) {
|
|
200
|
+
if (route.type === 'endpoint') {
|
|
201
|
+
get_group(route.id).endpoint = route.file;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return groups;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
*
|
|
210
|
+
* @param {import('types').ValidatedConfig} config
|
|
211
|
+
* @param {import('types').ManifestData} manifest_data
|
|
212
|
+
* @param {string} routes_dir
|
|
213
|
+
* @param {string} dir
|
|
214
|
+
* @param {Map<string, NodeGroup>} groups
|
|
215
|
+
* @param {import('typescript')} ts
|
|
216
|
+
*/
|
|
217
|
+
function write_types_for_dir(config, manifest_data, routes_dir, dir, groups, ts) {
|
|
218
|
+
const group = groups.get(dir);
|
|
219
|
+
if (!group) {
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const outdir = `${config.kit.outDir}/types/${routes_dir}/${dir}`;
|
|
224
|
+
|
|
225
|
+
const imports = [`import type * as Kit from '@sveltejs/kit';`];
|
|
226
|
+
|
|
227
|
+
/** @type {string[]} */
|
|
228
|
+
const written_files = [];
|
|
229
|
+
|
|
230
|
+
/** @type {string[]} */
|
|
231
|
+
const declarations = [];
|
|
232
|
+
|
|
233
|
+
/** @type {string[]} */
|
|
234
|
+
const exports = [];
|
|
235
|
+
|
|
236
|
+
const route_params = parse_route_id(dir).names;
|
|
237
|
+
|
|
238
|
+
if (route_params.length > 0) {
|
|
239
|
+
const params = route_params.map((param) => `${param}: string`).join('; ');
|
|
240
|
+
declarations.push(
|
|
241
|
+
`interface RouteParams extends Partial<Record<string, string>> { ${params} }`
|
|
242
|
+
);
|
|
243
|
+
} else {
|
|
244
|
+
declarations.push(`interface RouteParams extends Partial<Record<string, string>> {}`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (group.leaf) {
|
|
248
|
+
const { data, server_data, load, server_load, errors, written_proxies } = process_node(
|
|
249
|
+
ts,
|
|
250
|
+
group.leaf,
|
|
251
|
+
outdir,
|
|
252
|
+
'RouteParams',
|
|
253
|
+
groups
|
|
254
|
+
);
|
|
255
|
+
written_files.push(...written_proxies);
|
|
256
|
+
|
|
257
|
+
exports.push(`export type Errors = ${errors};`);
|
|
258
|
+
|
|
259
|
+
exports.push(`export type PageData = ${data};`);
|
|
260
|
+
if (load) {
|
|
261
|
+
exports.push(
|
|
262
|
+
`export type PageLoad<OutputData extends Record<string, any> | void = Record<string, any> | void> = ${load};`
|
|
263
|
+
);
|
|
264
|
+
exports.push('export type PageLoadEvent = Parameters<PageLoad>[0];');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
exports.push(`export type PageServerData = ${server_data};`);
|
|
268
|
+
if (server_load) {
|
|
269
|
+
exports.push(
|
|
270
|
+
`export type PageServerLoad<OutputData extends Record<string, any> | void = Record<string, any> | void> = ${server_load};`
|
|
271
|
+
);
|
|
272
|
+
exports.push('export type PageServerLoadEvent = Parameters<PageServerLoad>[0];');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (group.leaf.server) {
|
|
276
|
+
exports.push(`export type Action = Kit.Action<RouteParams>`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (group.default_layout || group.named_layouts.size > 0) {
|
|
281
|
+
// TODO to be completely rigorous, we should have a LayoutParams per
|
|
282
|
+
// layout, and only include params for child pages that use each layout.
|
|
283
|
+
// but that's more work than i care to do right now
|
|
284
|
+
const layout_params = new Set();
|
|
285
|
+
manifest_data.routes.forEach((route) => {
|
|
286
|
+
if (route.type === 'page' && route.id.startsWith(dir + '/')) {
|
|
287
|
+
// TODO this is O(n^2), see if we need to speed it up
|
|
288
|
+
for (const name of parse_route_id(route.id.slice(dir.length + 1)).names) {
|
|
289
|
+
layout_params.add(name);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
if (layout_params.size > 0) {
|
|
295
|
+
const params = Array.from(layout_params).map((param) => `${param}?: string`);
|
|
296
|
+
declarations.push(`interface LayoutParams extends RouteParams { ${params.join('; ')} }`);
|
|
297
|
+
} else {
|
|
298
|
+
declarations.push(`interface LayoutParams extends RouteParams {}`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (group.default_layout) {
|
|
302
|
+
const { data, server_data, load, server_load, written_proxies } = process_node(
|
|
303
|
+
ts,
|
|
304
|
+
group.default_layout,
|
|
305
|
+
outdir,
|
|
306
|
+
'LayoutParams',
|
|
307
|
+
groups
|
|
308
|
+
);
|
|
309
|
+
written_files.push(...written_proxies);
|
|
310
|
+
|
|
311
|
+
exports.push(`export type LayoutData = ${data};`);
|
|
312
|
+
if (load) {
|
|
313
|
+
exports.push(
|
|
314
|
+
`export type LayoutLoad<OutputData extends Record<string, any> | void = Record<string, any> | void> = ${load};`
|
|
315
|
+
);
|
|
316
|
+
exports.push('export type LayoutLoadEvent = Parameters<LayoutLoad>[0];');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
exports.push(`export type LayoutServerData = ${server_data};`);
|
|
320
|
+
if (server_load) {
|
|
321
|
+
exports.push(
|
|
322
|
+
`export type LayoutServerLoad<OutputData extends Record<string, any> | void = Record<string, any> | void> = ${server_load};`
|
|
323
|
+
);
|
|
324
|
+
exports.push('export type LayoutServerLoadEvent = Parameters<LayoutServerLoad>[0];');
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (group.named_layouts.size > 0) {
|
|
329
|
+
/** @type {string[]} */
|
|
330
|
+
const data_exports = [];
|
|
331
|
+
|
|
332
|
+
/** @type {string[]} */
|
|
333
|
+
const server_data_exports = [];
|
|
334
|
+
|
|
335
|
+
/** @type {string[]} */
|
|
336
|
+
const load_exports = [];
|
|
337
|
+
|
|
338
|
+
/** @type {string[]} */
|
|
339
|
+
const load_event_exports = [];
|
|
340
|
+
|
|
341
|
+
/** @type {string[]} */
|
|
342
|
+
const server_load_exports = [];
|
|
343
|
+
|
|
344
|
+
/** @type {string[]} */
|
|
345
|
+
const server_load_event_exports = [];
|
|
346
|
+
|
|
347
|
+
for (const [name, node] of group.named_layouts) {
|
|
348
|
+
const { data, server_data, load, server_load, written_proxies } = process_node(
|
|
349
|
+
ts,
|
|
350
|
+
node,
|
|
351
|
+
outdir,
|
|
352
|
+
'LayoutParams',
|
|
353
|
+
groups
|
|
354
|
+
);
|
|
355
|
+
written_files.push(...written_proxies);
|
|
356
|
+
data_exports.push(`export type ${name} = ${data};`);
|
|
357
|
+
server_data_exports.push(`export type ${name} = ${server_data};`);
|
|
358
|
+
if (load) {
|
|
359
|
+
load_exports.push(
|
|
360
|
+
`export type ${name}<OutputData extends Record<string, any> | void = Record<string, any> | void> = ${load};`
|
|
361
|
+
);
|
|
362
|
+
load_event_exports.push(`export type ${name} = Parameters<LayoutLoad.${name}>[0];`);
|
|
363
|
+
}
|
|
364
|
+
if (server_load) {
|
|
365
|
+
server_load_exports.push(
|
|
366
|
+
`export type ${name}<OutputData extends Record<string, any> | void = Record<string, any> | void> = ${server_load};`
|
|
367
|
+
);
|
|
368
|
+
server_load_event_exports.push(
|
|
369
|
+
`export type ${name} = Parameters<LayoutServerLoad.${name}>[0];`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
exports.push(`\nexport namespace LayoutData {\n\t${data_exports.join('\n\t')}\n}`);
|
|
375
|
+
exports.push(`\nexport namespace LayoutLoad {\n\t${load_exports.join('\n\t')}\n}`);
|
|
376
|
+
exports.push(`\nexport namespace LayoutLoadEvent {\n\t${load_event_exports.join('\n\t')}\n}`);
|
|
377
|
+
exports.push(
|
|
378
|
+
`\nexport namespace LayoutServerData {\n\t${server_data_exports.join('\n\t')}\n}`
|
|
379
|
+
);
|
|
380
|
+
exports.push(
|
|
381
|
+
`\nexport namespace LayoutServerLoad {\n\t${server_load_exports.join('\n\t')}\n}`
|
|
382
|
+
);
|
|
383
|
+
exports.push(
|
|
384
|
+
`\nexport namespace LayoutServerLoadEvent {\n\t${server_load_event_exports.join('\n\t')}\n}`
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (group.endpoint) {
|
|
390
|
+
exports.push(`export type RequestHandler = Kit.RequestHandler<RouteParams>;`);
|
|
391
|
+
exports.push(`export type RequestEvent = Kit.RequestEvent<RouteParams>;`);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const output = [imports.join('\n'), declarations.join('\n'), exports.join('\n')]
|
|
395
|
+
.filter(Boolean)
|
|
396
|
+
.join('\n\n');
|
|
397
|
+
|
|
398
|
+
written_files.push(write(`${outdir}/$types.d.ts`, output));
|
|
399
|
+
|
|
400
|
+
return written_files;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* @param {import('typescript')} ts
|
|
405
|
+
* @param {Node} node
|
|
406
|
+
* @param {string} outdir
|
|
407
|
+
* @param {string} params
|
|
408
|
+
* @param {Map<string, NodeGroup>} groups
|
|
409
|
+
*/
|
|
410
|
+
function process_node(ts, node, outdir, params, groups) {
|
|
411
|
+
let data;
|
|
412
|
+
let load;
|
|
413
|
+
let server_load;
|
|
414
|
+
let errors;
|
|
415
|
+
|
|
416
|
+
/** @type {string[]} */
|
|
417
|
+
let written_proxies = [];
|
|
418
|
+
|
|
419
|
+
let server_data;
|
|
420
|
+
|
|
421
|
+
if (node.server) {
|
|
422
|
+
const content = fs.readFileSync(node.server, 'utf8');
|
|
423
|
+
const proxy = tweak_types(ts, content, server_names);
|
|
424
|
+
const basename = path.basename(node.server);
|
|
425
|
+
if (proxy?.modified) {
|
|
426
|
+
written_proxies.push(write(`${outdir}/proxy${basename}`, proxy.code));
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
server_data = get_data_type(node.server, 'null', proxy);
|
|
430
|
+
server_load = `Kit.ServerLoad<${params}, ${get_parent_type('LayoutServerData')}, OutputData>`;
|
|
431
|
+
|
|
432
|
+
if (proxy) {
|
|
433
|
+
const types = [];
|
|
434
|
+
for (const method of ['POST', 'PUT', 'PATCH']) {
|
|
435
|
+
if (proxy.exports.includes(method)) {
|
|
436
|
+
// If the file wasn't tweaked, we can use the return type of the original file.
|
|
437
|
+
// The advantage is that type updates are reflected without saving.
|
|
438
|
+
const from = proxy.modified
|
|
439
|
+
? `./proxy${replace_ext_with_js(basename)}`
|
|
440
|
+
: path_to_original(outdir, node.server);
|
|
441
|
+
|
|
442
|
+
types.push(`Kit.AwaitedErrors<typeof import('${from}').${method}>`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
errors = types.length ? types.join(' | ') : 'null';
|
|
446
|
+
} else {
|
|
447
|
+
errors = 'unknown';
|
|
448
|
+
}
|
|
449
|
+
} else {
|
|
450
|
+
server_data = 'null';
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const parent_type = get_parent_type('LayoutData');
|
|
454
|
+
|
|
455
|
+
if (node.shared) {
|
|
456
|
+
const content = fs.readFileSync(node.shared, 'utf8');
|
|
457
|
+
const proxy = tweak_types(ts, content, shared_names);
|
|
458
|
+
if (proxy?.modified) {
|
|
459
|
+
written_proxies.push(write(`${outdir}/proxy${path.basename(node.shared)}`, proxy.code));
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const type = get_data_type(node.shared, `${parent_type} & ${server_data}`, proxy);
|
|
463
|
+
|
|
464
|
+
data = `Omit<${parent_type}, keyof ${type}> & ${type}`;
|
|
465
|
+
load = `Kit.Load<${params}, ${server_data}, ${parent_type}, OutputData>`;
|
|
466
|
+
} else if (server_data === 'null') {
|
|
467
|
+
data = parent_type;
|
|
468
|
+
} else {
|
|
469
|
+
data = `Omit<${parent_type}, keyof ${server_data}> & ${server_data}`;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return { data, server_data, load, server_load, errors, written_proxies };
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* @param {string} file_path
|
|
476
|
+
* @param {string} fallback
|
|
477
|
+
* @param {Proxy} proxy
|
|
478
|
+
*/
|
|
479
|
+
function get_data_type(file_path, fallback, proxy) {
|
|
480
|
+
if (proxy) {
|
|
481
|
+
if (proxy.exports.includes('load')) {
|
|
482
|
+
// If the file wasn't tweaked, we can use the return type of the original file.
|
|
483
|
+
// The advantage is that type updates are reflected without saving.
|
|
484
|
+
const from = proxy.modified
|
|
485
|
+
? `./proxy${replace_ext_with_js(path.basename(file_path))}`
|
|
486
|
+
: path_to_original(outdir, file_path);
|
|
487
|
+
return `Kit.AwaitedProperties<Awaited<ReturnType<typeof import('${from}').load>>>`;
|
|
488
|
+
} else {
|
|
489
|
+
return fallback;
|
|
490
|
+
}
|
|
491
|
+
} else {
|
|
492
|
+
return 'unknown';
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Get the parent type string by recursively looking up the parent layout and accumulate them to one type.
|
|
498
|
+
* @param {string} type
|
|
499
|
+
*/
|
|
500
|
+
function get_parent_type(type) {
|
|
501
|
+
const parent_imports = [];
|
|
502
|
+
let parent = node.parent;
|
|
503
|
+
let acc_diff = 0;
|
|
504
|
+
|
|
505
|
+
while (parent) {
|
|
506
|
+
acc_diff += parent.folder_depth_diff;
|
|
507
|
+
let parent_group = /** @type {NodeGroup} */ (groups.get(parent.key));
|
|
508
|
+
// unshift because we need it the other way round for the import string
|
|
509
|
+
parent_imports.unshift(
|
|
510
|
+
(acc_diff === 0 ? '' : `import('` + '../'.repeat(acc_diff) + '$types.js' + `').`) +
|
|
511
|
+
`${type}${parent.name ? `.${parent.name}` : ''}`
|
|
512
|
+
);
|
|
513
|
+
let parent_layout = /** @type {Node} */ (
|
|
514
|
+
parent.name ? parent_group.named_layouts.get(parent.name) : parent_group.default_layout
|
|
515
|
+
);
|
|
516
|
+
parent = parent_layout.parent;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
let parent_str = parent_imports[0] || 'Record<never, never>';
|
|
520
|
+
for (let i = 1; i < parent_imports.length; i++) {
|
|
521
|
+
// Omit is necessary because a parent could have a property with the same key which would
|
|
522
|
+
// cause a type conflict. At runtime the child overwrites the parent property in this case,
|
|
523
|
+
// so reflect that in the type definition.
|
|
524
|
+
parent_str = `Omit<${parent_str}, keyof ${parent_imports[i]}> & ${parent_imports[i]}`;
|
|
525
|
+
}
|
|
526
|
+
return parent_str;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* @param {string} outdir
|
|
532
|
+
* @param {string} file_path
|
|
533
|
+
*/
|
|
534
|
+
function path_to_original(outdir, file_path) {
|
|
535
|
+
return posixify(path.relative(outdir, path.join(cwd, replace_ext_with_js(file_path))));
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* @param {string} file_path
|
|
540
|
+
*/
|
|
541
|
+
function replace_ext_with_js(file_path) {
|
|
542
|
+
// Another extension than `.js` (or nothing, but that fails with node16 moduleResolution)
|
|
543
|
+
// will result in TS failing to lookup the file
|
|
544
|
+
const ext = path.extname(file_path);
|
|
545
|
+
return file_path.slice(0, -ext.length) + '.js';
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* @param {import('typescript')} ts
|
|
550
|
+
* @param {string} content
|
|
551
|
+
* @param {Set<string>} names
|
|
552
|
+
* @returns {Proxy}
|
|
553
|
+
*/
|
|
554
|
+
export function tweak_types(ts, content, names) {
|
|
555
|
+
try {
|
|
556
|
+
let modified = false;
|
|
557
|
+
|
|
558
|
+
const ast = ts.createSourceFile(
|
|
559
|
+
'filename.ts',
|
|
560
|
+
content,
|
|
561
|
+
ts.ScriptTarget.Latest,
|
|
562
|
+
false,
|
|
563
|
+
ts.ScriptKind.TS
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
const code = new MagicString(content);
|
|
567
|
+
|
|
568
|
+
const exports = new Map();
|
|
569
|
+
|
|
570
|
+
ast.forEachChild((node) => {
|
|
571
|
+
if (
|
|
572
|
+
ts.isExportDeclaration(node) &&
|
|
573
|
+
node.exportClause &&
|
|
574
|
+
ts.isNamedExports(node.exportClause)
|
|
575
|
+
) {
|
|
576
|
+
node.exportClause.elements.forEach((element) => {
|
|
577
|
+
const exported = element.name;
|
|
578
|
+
if (names.has(element.name.text)) {
|
|
579
|
+
const local = element.propertyName || element.name;
|
|
580
|
+
exports.set(exported.text, local.text);
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
586
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text && names.has(node.name?.text)) {
|
|
587
|
+
exports.set(node.name.text, node.name.text);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (ts.isVariableStatement(node)) {
|
|
591
|
+
node.declarationList.declarations.forEach((declaration) => {
|
|
592
|
+
if (ts.isIdentifier(declaration.name) && names.has(declaration.name.text)) {
|
|
593
|
+
exports.set(declaration.name.text, declaration.name.text);
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* @param {import('typescript').Node} node
|
|
602
|
+
* @param {import('typescript').Node} value
|
|
603
|
+
*/
|
|
604
|
+
function replace_jsdoc_type_tags(node, value) {
|
|
605
|
+
// @ts-ignore
|
|
606
|
+
if (node.jsDoc) {
|
|
607
|
+
// @ts-ignore
|
|
608
|
+
for (const comment of node.jsDoc) {
|
|
609
|
+
for (const tag of comment.tags) {
|
|
610
|
+
if (ts.isJSDocTypeTag(tag)) {
|
|
611
|
+
const is_fn =
|
|
612
|
+
ts.isFunctionDeclaration(value) ||
|
|
613
|
+
ts.isFunctionExpression(value) ||
|
|
614
|
+
ts.isArrowFunction(value);
|
|
615
|
+
|
|
616
|
+
if (is_fn && value.parameters?.length > 0) {
|
|
617
|
+
code.overwrite(tag.tagName.pos, tag.tagName.end, 'param');
|
|
618
|
+
code.prependRight(tag.typeExpression.pos + 1, 'Parameters<');
|
|
619
|
+
code.appendLeft(tag.typeExpression.end - 1, '>[0]');
|
|
620
|
+
code.appendLeft(tag.typeExpression.end, ' event');
|
|
621
|
+
} else {
|
|
622
|
+
code.overwrite(tag.pos, tag.end, '');
|
|
623
|
+
}
|
|
624
|
+
modified = true;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
ast.forEachChild((node) => {
|
|
632
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text && names.has(node.name?.text)) {
|
|
633
|
+
// remove JSDoc comment above `export function load ...`
|
|
634
|
+
replace_jsdoc_type_tags(node, node);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (ts.isVariableStatement(node)) {
|
|
638
|
+
// remove JSDoc comment above `export const load = ...`
|
|
639
|
+
if (
|
|
640
|
+
ts.isIdentifier(node.declarationList.declarations[0].name) &&
|
|
641
|
+
names.has(node.declarationList.declarations[0].name.text) &&
|
|
642
|
+
node.declarationList.declarations[0].initializer
|
|
643
|
+
) {
|
|
644
|
+
replace_jsdoc_type_tags(node, node.declarationList.declarations[0].initializer);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
for (const declaration of node.declarationList.declarations) {
|
|
648
|
+
if (
|
|
649
|
+
ts.isIdentifier(declaration.name) &&
|
|
650
|
+
names.has(declaration.name.text) &&
|
|
651
|
+
declaration.initializer
|
|
652
|
+
) {
|
|
653
|
+
// edge case — remove JSDoc comment above individual export
|
|
654
|
+
replace_jsdoc_type_tags(declaration, declaration.initializer);
|
|
655
|
+
|
|
656
|
+
// remove type from `export const load: Load ...`
|
|
657
|
+
if (declaration.type) {
|
|
658
|
+
let a = declaration.type.pos;
|
|
659
|
+
let b = declaration.type.end;
|
|
660
|
+
while (/\s/.test(content[a])) a += 1;
|
|
661
|
+
|
|
662
|
+
const type = content.slice(a, b);
|
|
663
|
+
code.remove(declaration.name.end, declaration.type.end);
|
|
664
|
+
|
|
665
|
+
const rhs = declaration.initializer;
|
|
666
|
+
|
|
667
|
+
if (
|
|
668
|
+
rhs &&
|
|
669
|
+
(ts.isArrowFunction(rhs) || ts.isFunctionExpression(rhs)) &&
|
|
670
|
+
rhs.parameters.length
|
|
671
|
+
) {
|
|
672
|
+
const arg = rhs.parameters[0];
|
|
673
|
+
|
|
674
|
+
const add_parens = content[arg.pos - 1] !== '(';
|
|
675
|
+
|
|
676
|
+
if (add_parens) code.prependRight(arg.pos, '(');
|
|
677
|
+
|
|
678
|
+
if (arg && !arg.type) {
|
|
679
|
+
code.appendLeft(
|
|
680
|
+
arg.name.end,
|
|
681
|
+
`: Parameters<${type}>[0]` + (add_parens ? ')' : '')
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
modified = true;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
return {
|
|
694
|
+
modified,
|
|
695
|
+
code: code.toString(),
|
|
696
|
+
exports: Array.from(exports.keys())
|
|
697
|
+
};
|
|
698
|
+
} catch {
|
|
699
|
+
return null;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* @param {string} file
|
|
705
|
+
* @param {string} content
|
|
706
|
+
*/
|
|
707
|
+
function write(file, content) {
|
|
708
|
+
write_if_changed(file, content);
|
|
709
|
+
return file;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Finds the nearest layout for given node.
|
|
714
|
+
* Assumes that nodes is sorted by path length (lowest first).
|
|
715
|
+
*
|
|
716
|
+
* @param {string} routes_dir
|
|
717
|
+
* @param {import('types').PageNode[]} nodes
|
|
718
|
+
* @param {number} start_idx
|
|
719
|
+
*/
|
|
720
|
+
export function find_nearest_layout(routes_dir, nodes, start_idx) {
|
|
721
|
+
const start_file = /** @type {string} */ (
|
|
722
|
+
nodes[start_idx].component || nodes[start_idx].shared || nodes[start_idx].server
|
|
723
|
+
);
|
|
724
|
+
|
|
725
|
+
let name = '';
|
|
726
|
+
const match = /^\+(layout|page)(?:-([^@.]+))?(?:@([^@.]+))?/.exec(path.basename(start_file));
|
|
727
|
+
if (!match) throw new Error(`Unexpected route file: ${start_file}`);
|
|
728
|
+
if (match[3] && match[3] !== 'default') {
|
|
729
|
+
name = match[3]; // a named layout is referenced
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
let common_path = path.dirname(start_file);
|
|
733
|
+
if (match[1] === 'layout' && !match[2] && !name) {
|
|
734
|
+
// We are a default layout, so we skip the current level
|
|
735
|
+
common_path = path.dirname(common_path);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
for (let i = start_idx - 1; i >= 0; i -= 1) {
|
|
739
|
+
const node = nodes[i];
|
|
740
|
+
const file = /** @type {string} */ (node.component || node.shared || node.server);
|
|
741
|
+
|
|
742
|
+
const current_path = path.dirname(file);
|
|
743
|
+
const common_path_length = common_path.split('/').length;
|
|
744
|
+
const current_path_length = current_path.split('/').length;
|
|
745
|
+
|
|
746
|
+
if (common_path_length < current_path_length) {
|
|
747
|
+
// this is a layout in a different tree
|
|
748
|
+
continue;
|
|
749
|
+
} else if (common_path_length > current_path_length) {
|
|
750
|
+
// we've gone back up a folder level
|
|
751
|
+
common_path = path.dirname(common_path);
|
|
752
|
+
}
|
|
753
|
+
if (common_path !== current_path) {
|
|
754
|
+
// this is a layout in a different tree
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
if (
|
|
758
|
+
path.basename(file, path.extname(file)).split('@')[0] !==
|
|
759
|
+
'+layout' + (name ? `-${name}` : '')
|
|
760
|
+
) {
|
|
761
|
+
// this is not the layout we are searching for
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// matching parent layout found
|
|
766
|
+
let folder_depth_diff =
|
|
767
|
+
posixify(path.relative(path.dirname(start_file), common_path + '/$types.js')).split('/')
|
|
768
|
+
.length - 1;
|
|
769
|
+
return {
|
|
770
|
+
key: path.dirname(file).slice(routes_dir.length + 1),
|
|
771
|
+
name,
|
|
772
|
+
folder_depth_diff
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
}
|