@sveltejs/kit 1.0.0-next.471 → 1.0.0-next.474

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.471",
3
+ "version": "1.0.0-next.474",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -171,20 +171,10 @@ const options = object(
171
171
 
172
172
  inlineStyleThreshold: number(0),
173
173
 
174
- methodOverride: object({
175
- parameter: string('_method'),
176
- allowed: validate([], (input, keypath) => {
177
- if (!Array.isArray(input) || !input.every((method) => typeof method === 'string')) {
178
- throw new Error(`${keypath} must be an array of strings`);
179
- }
180
-
181
- if (input.map((i) => i.toUpperCase()).includes('GET')) {
182
- throw new Error(`${keypath} cannot contain "GET"`);
183
- }
184
-
185
- return input;
186
- })
187
- }),
174
+ methodOverride: error(
175
+ () =>
176
+ 'Method overrides have been removed in favor of actions. See the PR for more information: https://github.com/sveltejs/kit/pull/6469'
177
+ ),
188
178
 
189
179
  moduleExtensions: string_array(['.js', '.ts']),
190
180
 
@@ -21,16 +21,16 @@ export function write_root(manifest_data, output) {
21
21
 
22
22
  let l = max_depth;
23
23
 
24
- let pyramid = `<svelte:component this={components[${l}]} data={data_${l}} {errors}/>`;
24
+ let pyramid = `<svelte:component this={components[${l}]} data={data_${l}} {form} />`;
25
25
 
26
26
  while (l--) {
27
27
  pyramid = `
28
28
  {#if components[${l + 1}]}
29
- <svelte:component this={components[${l}]} data={data_${l}} {errors}>
29
+ <svelte:component this={components[${l}]} data={data_${l}}>
30
30
  ${pyramid.replace(/\n/g, '\n\t\t\t\t\t')}
31
31
  </svelte:component>
32
32
  {:else}
33
- <svelte:component this={components[${l}]} data={data_${l}} {errors} />
33
+ <svelte:component this={components[${l}]} data={data_${l}} {form} />
34
34
  {/if}
35
35
  `
36
36
  .replace(/^\t\t\t/gm, '')
@@ -50,8 +50,8 @@ export function write_root(manifest_data, output) {
50
50
  export let page;
51
51
 
52
52
  export let components;
53
+ export let form;
53
54
  ${levels.map((l) => `export let data_${l} = null;`).join('\n\t\t\t\t')}
54
- export let errors;
55
55
 
56
56
  if (!browser) {
57
57
  setContext('__svelte__', stores);
@@ -21,9 +21,6 @@ try {
21
21
 
22
22
  const cwd = process.cwd();
23
23
 
24
- const shared_names = new Set(['load']);
25
- const server_names = new Set(['load', 'POST', 'PUT', 'PATCH', 'DELETE']); // TODO replace with a single `action`
26
-
27
24
  /**
28
25
  * Creates types for the whole manifest
29
26
  * @param {import('types').ValidatedConfig} config
@@ -193,6 +190,7 @@ function update_types(config, routes, route) {
193
190
 
194
191
  if (route.leaf.server) {
195
192
  exports.push(`export type Action = Kit.Action<RouteParams>`);
193
+ exports.push(`export type Actions = Kit.Actions<RouteParams>`);
196
194
  }
197
195
  }
198
196
 
@@ -231,6 +229,9 @@ function update_types(config, routes, route) {
231
229
 
232
230
  if (route.endpoint) {
233
231
  exports.push(`export type RequestHandler = Kit.RequestHandler<RouteParams>;`);
232
+ }
233
+
234
+ if (route.leaf?.server || route.endpoint) {
234
235
  exports.push(`export type RequestEvent = Kit.RequestEvent<RouteParams>;`);
235
236
  }
236
237
 
@@ -270,7 +271,7 @@ function process_node(node, outdir, is_page, all_pages_have_load = true) {
270
271
 
271
272
  if (node.server) {
272
273
  const content = fs.readFileSync(node.server, 'utf8');
273
- const proxy = tweak_types(content, server_names);
274
+ const proxy = tweak_types(content, true);
274
275
  const basename = path.basename(node.server);
275
276
  if (proxy?.modified) {
276
277
  fs.writeFileSync(`${outdir}/proxy${basename}`, proxy.code);
@@ -295,23 +296,19 @@ function process_node(node, outdir, is_page, all_pages_have_load = true) {
295
296
  exports.push(`export type ${prefix}ServerLoadEvent = Parameters<${prefix}ServerLoad>[0];`);
296
297
 
297
298
  if (is_page) {
298
- let errors = 'unknown';
299
+ let type = 'unknown';
299
300
  if (proxy) {
300
- const types = [];
301
- for (const method of ['POST', 'PUT', 'PATCH', 'DELETE']) {
302
- if (proxy.exports.includes(method)) {
303
- // If the file wasn't tweaked, we can use the return type of the original file.
304
- // The advantage is that type updates are reflected without saving.
305
- const from = proxy.modified
306
- ? `./proxy${replace_ext_with_js(basename)}`
307
- : path_to_original(outdir, node.server);
308
-
309
- types.push(`Kit.AwaitedErrors<typeof import('${from}').${method}>`);
310
- }
301
+ if (proxy.exports.includes('actions')) {
302
+ // If the file wasn't tweaked, we can use the return type of the original file.
303
+ // The advantage is that type updates are reflected without saving.
304
+ const from = proxy.modified
305
+ ? `./proxy${replace_ext_with_js(basename)}`
306
+ : path_to_original(outdir, node.server);
307
+
308
+ type = `Kit.AwaitedActions<typeof import('${from}').actions>`;
311
309
  }
312
- errors = types.length ? types.join(' | ') : 'null';
313
310
  }
314
- exports.push(`export type Errors = ${errors};`);
311
+ exports.push(`export type ActionData = ${type};`);
315
312
  }
316
313
  } else {
317
314
  server_data = 'null';
@@ -323,7 +320,7 @@ function process_node(node, outdir, is_page, all_pages_have_load = true) {
323
320
 
324
321
  if (node.shared) {
325
322
  const content = fs.readFileSync(node.shared, 'utf8');
326
- const proxy = tweak_types(content, shared_names);
323
+ const proxy = tweak_types(content, false);
327
324
  if (proxy?.modified) {
328
325
  fs.writeFileSync(`${outdir}/proxy${path.basename(node.shared)}`, proxy.code);
329
326
  written_proxies.push(`proxy${path.basename(node.shared)}`);
@@ -426,10 +423,12 @@ function replace_ext_with_js(file_path) {
426
423
 
427
424
  /**
428
425
  * @param {string} content
429
- * @param {Set<string>} names
426
+ * @param {boolean} is_server
430
427
  * @returns {Proxy}
431
428
  */
432
- export function tweak_types(content, names) {
429
+ export function tweak_types(content, is_server) {
430
+ const names = new Set(is_server ? ['load', 'actions'] : ['load']);
431
+
433
432
  try {
434
433
  let modified = false;
435
434
 
@@ -480,6 +479,7 @@ export function tweak_types(content, names) {
480
479
  * @param {import('typescript').Node} value
481
480
  */
482
481
  function replace_jsdoc_type_tags(node, value) {
482
+ let _modified = false;
483
483
  // @ts-ignore
484
484
  if (node.jsDoc) {
485
485
  // @ts-ignore
@@ -492,22 +492,27 @@ export function tweak_types(content, names) {
492
492
  ts.isArrowFunction(value);
493
493
 
494
494
  if (is_fn && value.parameters?.length > 0) {
495
+ const name = ts.isIdentifier(value.parameters[0].name)
496
+ ? value.parameters[0].name.text
497
+ : 'event';
495
498
  code.overwrite(tag.tagName.pos, tag.tagName.end, 'param');
496
499
  code.prependRight(tag.typeExpression.pos + 1, 'Parameters<');
497
500
  code.appendLeft(tag.typeExpression.end - 1, '>[0]');
498
- code.appendLeft(tag.typeExpression.end, ' event');
501
+ code.appendLeft(tag.typeExpression.end, ` ${name}`);
499
502
  } else {
500
503
  code.overwrite(tag.pos, tag.end, '');
501
504
  }
502
- modified = true;
505
+ _modified = true;
503
506
  }
504
507
  }
505
508
  }
506
509
  }
510
+ modified ||= _modified;
511
+ return _modified;
507
512
  }
508
513
 
509
514
  ast.forEachChild((node) => {
510
- if (ts.isFunctionDeclaration(node) && node.name?.text && names.has(node.name?.text)) {
515
+ if (ts.isFunctionDeclaration(node) && node.name?.text && node.name?.text === 'load') {
511
516
  // remove JSDoc comment above `export function load ...`
512
517
  replace_jsdoc_type_tags(node, node);
513
518
  }
@@ -525,7 +530,7 @@ export function tweak_types(content, names) {
525
530
  for (const declaration of node.declarationList.declarations) {
526
531
  if (
527
532
  ts.isIdentifier(declaration.name) &&
528
- names.has(declaration.name.text) &&
533
+ declaration.name.text === 'load' &&
529
534
  declaration.initializer
530
535
  ) {
531
536
  // edge case — remove JSDoc comment above individual export
@@ -548,7 +553,6 @@ export function tweak_types(content, names) {
548
553
  rhs.parameters.length
549
554
  ) {
550
555
  const arg = rhs.parameters[0];
551
-
552
556
  const add_parens = content[arg.pos - 1] !== '(';
553
557
 
554
558
  if (add_parens) code.prependRight(arg.pos, '(');
@@ -569,6 +573,80 @@ export function tweak_types(content, names) {
569
573
 
570
574
  modified = true;
571
575
  }
576
+ } else if (
577
+ is_server &&
578
+ ts.isIdentifier(declaration.name) &&
579
+ declaration.name?.text === 'actions' &&
580
+ declaration.initializer
581
+ ) {
582
+ // remove JSDoc comment from `export const actions = ..`
583
+ const removed = replace_jsdoc_type_tags(node, declaration.initializer);
584
+ // ... and move type to each individual action
585
+ if (removed) {
586
+ const rhs = declaration.initializer;
587
+ if (ts.isObjectLiteralExpression(rhs)) {
588
+ for (const prop of rhs.properties) {
589
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
590
+ const rhs = prop.initializer;
591
+ const replaced = replace_jsdoc_type_tags(prop, rhs);
592
+ if (
593
+ !replaced &&
594
+ rhs &&
595
+ (ts.isArrowFunction(rhs) || ts.isFunctionExpression(rhs)) &&
596
+ rhs.parameters?.[0]
597
+ ) {
598
+ const name = ts.isIdentifier(rhs.parameters[0].name)
599
+ ? rhs.parameters[0].name.text
600
+ : 'event';
601
+ code.prependRight(
602
+ rhs.pos,
603
+ `/** @param {import('./$types').RequestEvent} ${name} */ `
604
+ );
605
+ }
606
+ }
607
+ }
608
+ }
609
+ }
610
+
611
+ // remove type from `export const actions: Actions ...`
612
+ if (declaration.type) {
613
+ let a = declaration.type.pos;
614
+ let b = declaration.type.end;
615
+ while (/\s/.test(content[a])) a += 1;
616
+
617
+ const type = content.slice(a, b);
618
+ code.remove(declaration.name.end, declaration.type.end);
619
+ code.append(`;null as any as ${type};`);
620
+ modified = true;
621
+
622
+ // ... and move type to each individual action
623
+ const rhs = declaration.initializer;
624
+ if (ts.isObjectLiteralExpression(rhs)) {
625
+ for (const prop of rhs.properties) {
626
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
627
+ const rhs = prop.initializer;
628
+
629
+ if (
630
+ rhs &&
631
+ (ts.isArrowFunction(rhs) || ts.isFunctionExpression(rhs)) &&
632
+ rhs.parameters.length
633
+ ) {
634
+ const arg = rhs.parameters[0];
635
+ const add_parens = content[arg.pos - 1] !== '(';
636
+
637
+ if (add_parens) code.prependRight(arg.pos, '(');
638
+
639
+ if (arg && !arg.type) {
640
+ code.appendLeft(
641
+ arg.name.end,
642
+ `: import('./$types').RequestEvent` + (add_parens ? ')' : '')
643
+ );
644
+ }
645
+ }
646
+ }
647
+ }
648
+ }
649
+ }
572
650
  }
573
651
  }
574
652
  }
@@ -1,4 +1,4 @@
1
- import { HttpError, Redirect } from '../runtime/control.js';
1
+ import { HttpError, Redirect, ValidationError } from '../runtime/control.js';
2
2
 
3
3
  /**
4
4
  * Creates an `HttpError` object with an HTTP status code and an optional message.
@@ -43,3 +43,12 @@ export function json(data, init) {
43
43
  headers
44
44
  });
45
45
  }
46
+
47
+ /**
48
+ * Generates a `ValidationError` object.
49
+ * @param {number} status
50
+ * @param {Record<string, any> | undefined} [data]
51
+ */
52
+ export function invalid(status, data) {
53
+ return new ValidationError(status, data);
54
+ }
@@ -1,7 +1,7 @@
1
1
  import { fetch, Response, Request, Headers } from 'undici';
2
2
  import { ReadableStream, TransformStream, WritableStream } from 'stream/web';
3
3
  import { Readable } from 'stream';
4
- import { Request as NodeFetchRequest } from 'node-fetch';
4
+ import { Request as NodeFetchRequest, FormData } from 'node-fetch';
5
5
  import { webcrypto as crypto } from 'crypto';
6
6
 
7
7
  /** @type {Record<string, any>} */
@@ -24,7 +24,8 @@ const globals = {
24
24
  Headers,
25
25
  ReadableStream,
26
26
  TransformStream,
27
- WritableStream
27
+ WritableStream,
28
+ FormData
28
29
  };
29
30
 
30
31
  // exported for dev/preview and node environments
@@ -74,7 +74,6 @@ export class Server {
74
74
  },
75
75
  hooks: null,
76
76
  manifest,
77
- method_override: ${s(config.kit.methodOverride)},
78
77
  paths: { base, assets },
79
78
  public_env: {},
80
79
  read,
@@ -415,7 +415,6 @@ export async function dev(vite, vite_config, svelte_config) {
415
415
  },
416
416
  hooks,
417
417
  manifest,
418
- method_override: svelte_config.kit.methodOverride,
419
418
  paths: {
420
419
  base: svelte_config.kit.paths.base,
421
420
  assets
@@ -0,0 +1,65 @@
1
+ import { invalidateAll } from './navigation.js';
2
+ import { client } from '../client/singletons.js';
3
+
4
+ /**
5
+ * @param {string} name
6
+ */
7
+ function guard(name) {
8
+ return () => {
9
+ throw new Error(`Cannot call ${name}(...) on the server`);
10
+ };
11
+ }
12
+
13
+ const ssr = import.meta.env.SSR;
14
+
15
+ /** @type {import('$app/forms').applyAction} */
16
+ export const applyAction = ssr ? guard('applyAction') : client.apply_action;
17
+
18
+ /** @type {import('$app/forms').enhance} */
19
+ export function enhance(form, submit = () => {}) {
20
+ /** @param {import('types').ActionResult} result */
21
+ const fallback_callback = async (result) => {
22
+ if (result.type === 'success') {
23
+ await invalidateAll();
24
+ }
25
+
26
+ if (location.origin + location.pathname === form.action.split('?')[0]) {
27
+ applyAction(result);
28
+ }
29
+ };
30
+
31
+ /** @param {SubmitEvent} event */
32
+ async function handle_submit(event) {
33
+ event.preventDefault();
34
+
35
+ const data = new FormData(form);
36
+
37
+ let cancelled = false;
38
+ const cancel = () => (cancelled = true);
39
+
40
+ const callback = submit({ form, data, cancel }) ?? fallback_callback;
41
+ if (cancelled) return;
42
+
43
+ try {
44
+ const response = await fetch(form.action, {
45
+ method: 'POST',
46
+ headers: {
47
+ accept: 'application/json'
48
+ },
49
+ body: data
50
+ });
51
+
52
+ callback(await response.json());
53
+ } catch (error) {
54
+ callback({ type: 'error', error });
55
+ }
56
+ }
57
+
58
+ form.addEventListener('submit', handle_submit);
59
+
60
+ return {
61
+ destroy() {
62
+ form.removeEventListener('submit', handle_submit);
63
+ }
64
+ };
65
+ }