@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.
Files changed (104) hide show
  1. package/README.md +12 -9
  2. package/package.json +95 -63
  3. package/src/cli.js +112 -0
  4. package/src/core/adapt/builder.js +207 -0
  5. package/src/core/adapt/index.js +19 -0
  6. package/src/core/config/index.js +86 -0
  7. package/src/core/config/options.js +488 -0
  8. package/src/core/config/types.d.ts +1 -0
  9. package/src/core/constants.js +5 -0
  10. package/src/core/env.js +97 -0
  11. package/src/core/generate_manifest/index.js +99 -0
  12. package/src/core/prerender/crawl.js +194 -0
  13. package/src/core/prerender/prerender.js +378 -0
  14. package/src/core/prerender/queue.js +80 -0
  15. package/src/core/sync/create_manifest_data/index.js +506 -0
  16. package/src/core/sync/create_manifest_data/types.d.ts +40 -0
  17. package/src/core/sync/sync.js +59 -0
  18. package/src/core/sync/utils.js +44 -0
  19. package/src/core/sync/write_ambient.js +27 -0
  20. package/src/core/sync/write_client_manifest.js +82 -0
  21. package/src/core/sync/write_matchers.js +25 -0
  22. package/src/core/sync/write_root.js +91 -0
  23. package/src/core/sync/write_tsconfig.js +195 -0
  24. package/src/core/sync/write_types.js +775 -0
  25. package/src/core/utils.js +70 -0
  26. package/src/hooks.js +26 -0
  27. package/src/index/index.js +45 -0
  28. package/src/index/private.js +33 -0
  29. package/src/node/index.js +145 -0
  30. package/src/node/polyfills.js +40 -0
  31. package/src/runtime/app/env.js +11 -0
  32. package/src/runtime/app/navigation.js +22 -0
  33. package/src/runtime/app/paths.js +1 -0
  34. package/src/runtime/app/stores.js +102 -0
  35. package/src/runtime/client/ambient.d.ts +17 -0
  36. package/src/runtime/client/client.js +1289 -0
  37. package/src/runtime/client/fetcher.js +60 -0
  38. package/src/runtime/client/parse.js +36 -0
  39. package/src/runtime/client/singletons.js +21 -0
  40. package/src/runtime/client/start.js +46 -0
  41. package/src/runtime/client/types.d.ts +105 -0
  42. package/src/runtime/client/utils.js +113 -0
  43. package/src/runtime/components/error.svelte +16 -0
  44. package/{assets → src/runtime}/components/layout.svelte +0 -0
  45. package/src/runtime/env/dynamic/private.js +1 -0
  46. package/src/runtime/env/dynamic/public.js +1 -0
  47. package/src/runtime/env-private.js +7 -0
  48. package/src/runtime/env-public.js +7 -0
  49. package/src/runtime/env.js +6 -0
  50. package/src/runtime/hash.js +16 -0
  51. package/src/runtime/paths.js +11 -0
  52. package/src/runtime/server/endpoint.js +58 -0
  53. package/src/runtime/server/index.js +448 -0
  54. package/src/runtime/server/page/cookie.js +25 -0
  55. package/src/runtime/server/page/crypto.js +239 -0
  56. package/src/runtime/server/page/csp.js +249 -0
  57. package/src/runtime/server/page/fetch.js +266 -0
  58. package/src/runtime/server/page/index.js +416 -0
  59. package/src/runtime/server/page/load_data.js +135 -0
  60. package/src/runtime/server/page/render.js +362 -0
  61. package/src/runtime/server/page/respond_with_error.js +94 -0
  62. package/src/runtime/server/page/types.d.ts +44 -0
  63. package/src/runtime/server/utils.js +116 -0
  64. package/src/utils/error.js +22 -0
  65. package/src/utils/escape.js +104 -0
  66. package/src/utils/filesystem.js +108 -0
  67. package/src/utils/http.js +55 -0
  68. package/src/utils/misc.js +1 -0
  69. package/src/utils/routing.js +108 -0
  70. package/src/utils/url.js +97 -0
  71. package/src/vite/build/build_server.js +337 -0
  72. package/src/vite/build/build_service_worker.js +90 -0
  73. package/src/vite/build/utils.js +160 -0
  74. package/src/vite/dev/index.js +551 -0
  75. package/src/vite/index.js +574 -0
  76. package/src/vite/preview/index.js +186 -0
  77. package/src/vite/types.d.ts +3 -0
  78. package/src/vite/utils.js +345 -0
  79. package/svelte-kit.js +1 -1
  80. package/types/ambient.d.ts +357 -0
  81. package/types/index.d.ts +343 -0
  82. package/types/internal.d.ts +308 -0
  83. package/types/private.d.ts +209 -0
  84. package/CHANGELOG.md +0 -431
  85. package/assets/components/error.svelte +0 -13
  86. package/assets/runtime/app/env.js +0 -5
  87. package/assets/runtime/app/navigation.js +0 -41
  88. package/assets/runtime/app/paths.js +0 -1
  89. package/assets/runtime/app/stores.js +0 -93
  90. package/assets/runtime/chunks/utils.js +0 -19
  91. package/assets/runtime/internal/singletons.js +0 -23
  92. package/assets/runtime/internal/start.js +0 -770
  93. package/assets/runtime/paths.js +0 -12
  94. package/dist/.DS_Store +0 -0
  95. package/dist/chunks/index.js +0 -3521
  96. package/dist/chunks/index2.js +0 -587
  97. package/dist/chunks/index3.js +0 -246
  98. package/dist/chunks/index4.js +0 -538
  99. package/dist/chunks/index5.js +0 -761
  100. package/dist/chunks/index6.js +0 -322
  101. package/dist/chunks/standard.js +0 -99
  102. package/dist/chunks/utils.js +0 -83
  103. package/dist/cli.js +0 -546
  104. 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
+ }