@sveltejs/kit 1.0.0-next.503 → 1.0.0-next.505

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.503",
3
+ "version": "1.0.0-next.505",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -6,10 +6,18 @@ import { compact } from '../../../utils/array.js';
6
6
 
7
7
  /**
8
8
  * @typedef {{
9
+ * file_name: string;
9
10
  * modified: boolean;
10
11
  * code: string;
11
12
  * exports: any[];
12
13
  * } | null} Proxy
14
+ *
15
+ * @typedef {{
16
+ * server: Proxy,
17
+ * shared: Proxy
18
+ * }} Proxies
19
+ *
20
+ * @typedef {Map<import('types').PageNode, {route: import('types').RouteData, proxies: Proxies}>} RoutesMap
13
21
  */
14
22
 
15
23
  /** @type {import('typescript')} */
@@ -153,11 +161,11 @@ export async function write_types(config, manifest_data, file) {
153
161
  * @param {import('types').ManifestData} manifest_data
154
162
  */
155
163
  function create_routes_map(manifest_data) {
156
- /** @type {Map<import('types').PageNode, import('types').RouteData>} */
164
+ /** @type {RoutesMap} */
157
165
  const map = new Map();
158
166
  for (const route of manifest_data.routes) {
159
167
  if (route.leaf) {
160
- map.set(route.leaf, route);
168
+ map.set(route.leaf, { route, proxies: { server: null, shared: null } });
161
169
  }
162
170
  }
163
171
  return map;
@@ -166,7 +174,7 @@ function create_routes_map(manifest_data) {
166
174
  /**
167
175
  * Update types for a specific route
168
176
  * @param {import('types').ValidatedConfig} config
169
- * @param {Map<import('types').PageNode, import('types').RouteData>} routes
177
+ * @param {RoutesMap} routes
170
178
  * @param {import('types').RouteData} route
171
179
  * @param {Set<string>} [to_delete]
172
180
  */
@@ -208,12 +216,30 @@ function update_types(config, routes, route, to_delete = new Set()) {
208
216
  }
209
217
 
210
218
  if (route.leaf) {
211
- const { declarations: d, exports: e, written_proxies } = process_node(route.leaf, outdir, true);
219
+ let route_info = routes.get(route.leaf);
220
+ if (!route_info) {
221
+ // This should be defined, but belts and braces
222
+ route_info = { route, proxies: { server: null, shared: null } };
223
+ routes.set(route.leaf, route_info);
224
+ }
225
+
226
+ const {
227
+ declarations: d,
228
+ exports: e,
229
+ proxies
230
+ } = process_node(route.leaf, outdir, true, route_info.proxies);
212
231
 
213
232
  exports.push(...e);
214
233
  declarations.push(...d);
215
234
 
216
- for (const file of written_proxies) to_delete.delete(file);
235
+ if (proxies.server) {
236
+ route_info.proxies.server = proxies.server;
237
+ if (proxies.server?.modified) to_delete.delete(proxies.server.file_name);
238
+ }
239
+ if (proxies.shared) {
240
+ route_info.proxies.shared = proxies.shared;
241
+ if (proxies.shared?.modified) to_delete.delete(proxies.shared.file_name);
242
+ }
217
243
 
218
244
  if (route.leaf.server) {
219
245
  exports.push(`export type Action = Kit.Action<RouteParams>`);
@@ -227,9 +253,20 @@ function update_types(config, routes, route, to_delete = new Set()) {
227
253
  route.layout.child_pages?.forEach((page) => {
228
254
  const leaf = routes.get(page);
229
255
  if (leaf) {
230
- for (const name of leaf.names) {
256
+ for (const name of leaf.route.names) {
231
257
  layout_params.add(name);
232
258
  }
259
+
260
+ ensureProxies(page, leaf.proxies);
261
+
262
+ if (
263
+ // Be defensive - if a proxy doesn't exist (because it couldn't be created), assume a load function exists.
264
+ // If we didn't and it's a false negative, the user could wrongfully get a type error on layouts.
265
+ (leaf.proxies.server && !leaf.proxies.server.exports.includes('load')) ||
266
+ (leaf.proxies.shared && !leaf.proxies.shared.exports.includes('load'))
267
+ ) {
268
+ all_pages_have_load = false;
269
+ }
233
270
  }
234
271
  if (!page.server && !page.shared) {
235
272
  all_pages_have_load = false;
@@ -245,20 +282,27 @@ function update_types(config, routes, route, to_delete = new Set()) {
245
282
  const {
246
283
  exports: e,
247
284
  declarations: d,
248
- written_proxies
249
- } = process_node(route.layout, outdir, false, all_pages_have_load);
285
+ proxies
286
+ } = process_node(
287
+ route.layout,
288
+ outdir,
289
+ false,
290
+ { server: null, shared: null },
291
+ all_pages_have_load
292
+ );
250
293
 
251
294
  exports.push(...e);
252
295
  declarations.push(...d);
253
296
 
254
- for (const file of written_proxies) to_delete.delete(file);
297
+ if (proxies.server?.modified) to_delete.delete(proxies.server.file_name);
298
+ if (proxies.shared?.modified) to_delete.delete(proxies.shared.file_name);
255
299
  }
256
300
 
257
301
  if (route.endpoint) {
258
302
  exports.push(`export type RequestHandler = Kit.RequestHandler<RouteParams>;`);
259
303
  }
260
304
 
261
- if (route.leaf?.server || route.endpoint) {
305
+ if (route.leaf?.server || route.layout?.server || route.endpoint) {
262
306
  exports.push(`export type RequestEvent = Kit.RequestEvent<RouteParams>;`);
263
307
  }
264
308
 
@@ -278,14 +322,13 @@ function update_types(config, routes, route, to_delete = new Set()) {
278
322
  * @param {import('types').PageNode} node
279
323
  * @param {string} outdir
280
324
  * @param {boolean} is_page
325
+ * @param {Proxies} proxies
281
326
  * @param {boolean} [all_pages_have_load]
282
327
  */
283
- function process_node(node, outdir, is_page, all_pages_have_load = true) {
328
+ function process_node(node, outdir, is_page, proxies, all_pages_have_load = true) {
284
329
  const params = `${is_page ? 'Route' : 'Layout'}Params`;
285
330
  const prefix = is_page ? 'Page' : 'Layout';
286
331
 
287
- /** @type {string[]} */
288
- let written_proxies = [];
289
332
  /** @type {string[]} */
290
333
  const declarations = [];
291
334
  /** @type {string[]} */
@@ -296,13 +339,13 @@ function process_node(node, outdir, is_page, all_pages_have_load = true) {
296
339
  /** @type {string} */
297
340
  let data;
298
341
 
342
+ ensureProxies(node, proxies);
343
+
299
344
  if (node.server) {
300
- const content = fs.readFileSync(node.server, 'utf8');
301
- const proxy = tweak_types(content, true);
302
345
  const basename = path.basename(node.server);
346
+ const proxy = proxies.server;
303
347
  if (proxy?.modified) {
304
348
  fs.writeFileSync(`${outdir}/proxy${basename}`, proxy.code);
305
- written_proxies.push(`proxy${basename}`);
306
349
  }
307
350
 
308
351
  server_data = get_data_type(node.server, 'null', proxy, true);
@@ -346,11 +389,9 @@ function process_node(node, outdir, is_page, all_pages_have_load = true) {
346
389
  declarations.push(`type ${parent_type} = ${get_parent_type(node, 'LayoutData')};`);
347
390
 
348
391
  if (node.shared) {
349
- const content = fs.readFileSync(node.shared, 'utf8');
350
- const proxy = tweak_types(content, false);
392
+ const proxy = proxies.shared;
351
393
  if (proxy?.modified) {
352
394
  fs.writeFileSync(`${outdir}/proxy${path.basename(node.shared)}`, proxy.code);
353
- written_proxies.push(`proxy${path.basename(node.shared)}`);
354
395
  }
355
396
 
356
397
  const type = get_data_type(
@@ -378,7 +419,7 @@ function process_node(node, outdir, is_page, all_pages_have_load = true) {
378
419
 
379
420
  exports.push(`export type ${prefix}Data = ${data};`);
380
421
 
381
- return { declarations, exports, written_proxies };
422
+ return { declarations, exports, proxies };
382
423
 
383
424
  /**
384
425
  * @param {string} file_path
@@ -405,6 +446,42 @@ function process_node(node, outdir, is_page, all_pages_have_load = true) {
405
446
  }
406
447
  }
407
448
 
449
+ /**
450
+ * This function populates the proxies object, if necessary and not already done.
451
+ * Proxies are used to tweak the code of a file before it's typechecked.
452
+ * They are needed in two places - when generating the types for a page or layout.
453
+ * To not do the same work twice, we generate the proxies once and pass them around.
454
+ *
455
+ * @param {import('types').PageNode} node
456
+ * @param {Proxies} proxies
457
+ */
458
+ function ensureProxies(node, proxies) {
459
+ if (node.server && !proxies.server) {
460
+ proxies.server = createProxy(node.server, true);
461
+ }
462
+
463
+ if (node.shared && !proxies.shared) {
464
+ proxies.shared = createProxy(node.shared, false);
465
+ }
466
+ }
467
+
468
+ /**
469
+ * @param {string} file_path
470
+ * @param {boolean} is_server
471
+ * @returns {Proxy}
472
+ */
473
+ function createProxy(file_path, is_server) {
474
+ const proxy = tweak_types(fs.readFileSync(file_path, 'utf8'), is_server);
475
+ if (proxy) {
476
+ return {
477
+ ...proxy,
478
+ file_name: `proxy${path.basename(file_path)}`
479
+ };
480
+ } else {
481
+ return null;
482
+ }
483
+ }
484
+
408
485
  /**
409
486
  * Get the parent type string by recursively looking up the parent layout and accumulate them to one type.
410
487
  * @param {import('types').PageNode} node
@@ -457,7 +534,7 @@ function replace_ext_with_js(file_path) {
457
534
  /**
458
535
  * @param {string} content
459
536
  * @param {boolean} is_server
460
- * @returns {Proxy}
537
+ * @returns {Omit<NonNullable<Proxy>, 'file_name'> | null}
461
538
  */
462
539
  export function tweak_types(content, is_server) {
463
540
  const names = new Set(is_server ? ['load', 'actions'] : ['load']);
@@ -51,6 +51,12 @@ export function enhance(form, submit = () => {}) {
51
51
  );
52
52
 
53
53
  const data = new FormData(form);
54
+
55
+ const submitter_name = event.submitter?.getAttribute('name');
56
+ if (submitter_name) {
57
+ data.append(submitter_name, event.submitter?.getAttribute('value') ?? '');
58
+ }
59
+
54
60
  const controller = new AbortController();
55
61
 
56
62
  let cancelled = false;
package/types/index.d.ts CHANGED
@@ -59,7 +59,11 @@ export interface ValidationError<T extends Record<string, unknown> | undefined =
59
59
  data: T;
60
60
  }
61
61
 
62
- type UnpackValidationError<T> = T extends ValidationError<infer X> ? X : T;
62
+ type UnpackValidationError<T> = T extends ValidationError<infer X>
63
+ ? X
64
+ : T extends void
65
+ ? undefined // needs to be undefined, because void will corrupt union type
66
+ : T;
63
67
 
64
68
  export interface Builder {
65
69
  log: Logger;
@@ -295,13 +299,37 @@ export interface Navigation {
295
299
  delta?: number;
296
300
  }
297
301
 
302
+ /**
303
+ * The shape of the `$page` store
304
+ */
298
305
  export interface Page<Params extends Record<string, string> = Record<string, string>> {
306
+ /**
307
+ * The URL of the current page
308
+ */
299
309
  url: URL;
310
+ /**
311
+ * The parameters of the current page - e.g. for a route like `/blog/[slug]`, the `slug` parameter
312
+ */
300
313
  params: Params;
314
+ /**
315
+ * The route ID of the current page - e.g. for `src/routes/blog/[slug]`, it would be `blog/[slug]`
316
+ */
301
317
  routeId: string | null;
318
+ /**
319
+ * Http status code of the current page
320
+ */
302
321
  status: number;
322
+ /**
323
+ * The error object of the current page, if any. Filled from the `handleError` hooks.
324
+ */
303
325
  error: App.Error | null;
326
+ /**
327
+ * The merged result of all data from all `load` functions on the current page. You can type a common denominator through `App.PageData`.
328
+ */
304
329
  data: App.PageData & Record<string, any>;
330
+ /**
331
+ * Filled only after a form submission. See [form actions](https://kit.svelte.dev/docs/form-actions) for more info.
332
+ */
305
333
  form: any;
306
334
  }
307
335