@sveltejs/kit 1.0.0-next.511 → 1.0.0-next.513

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.511",
3
+ "version": "1.0.0-next.513",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -26,12 +26,12 @@
26
26
  "devDependencies": {
27
27
  "@playwright/test": "^1.25.0",
28
28
  "@types/connect": "^3.4.35",
29
- "@types/marked": "^4.0.3",
29
+ "@types/marked": "^4.0.7",
30
30
  "@types/mime": "^3.0.0",
31
31
  "@types/node": "^16.11.36",
32
32
  "@types/sade": "^1.7.4",
33
33
  "@types/set-cookie-parser": "^2.4.2",
34
- "marked": "^4.0.16",
34
+ "marked": "^4.1.1",
35
35
  "rollup": "^2.78.1",
36
36
  "svelte": "^3.48.0",
37
37
  "svelte-preprocess": "^4.10.6",
package/src/constants.js CHANGED
@@ -4,4 +4,4 @@ export const SVELTE_KIT_ASSETS = '/_svelte_kit_assets';
4
4
 
5
5
  export const GENERATED_COMMENT = '// this file is generated — do not edit it\n';
6
6
 
7
- export const DATA_SUFFIX = '/__data.js';
7
+ export const DATA_SUFFIX = '/__data.json';
@@ -5,6 +5,7 @@ import { pipeline } from 'stream';
5
5
  import { promisify } from 'util';
6
6
  import { copy, rimraf, mkdirp } from '../../utils/filesystem.js';
7
7
  import { generate_manifest } from '../generate_manifest/index.js';
8
+ import { affects_path } from '../../utils/routing.js';
8
9
 
9
10
  const pipe = promisify(pipeline);
10
11
 
@@ -46,11 +47,14 @@ export function create_builder({ config, build_data, routes, prerendered, log })
46
47
 
47
48
  return {
48
49
  id: route.id,
49
- segments: route.id.split('/').map((segment) => ({
50
- dynamic: segment.includes('['),
51
- rest: segment.includes('[...'),
52
- content: segment
53
- })),
50
+ segments: route.id
51
+ .split('/')
52
+ .filter(affects_path)
53
+ .map((segment) => ({
54
+ dynamic: segment.includes('['),
55
+ rest: segment.includes('[...'),
56
+ content: segment
57
+ })),
54
58
  pattern: route.pattern,
55
59
  methods: Array.from(methods)
56
60
  };
@@ -18,9 +18,17 @@ export function init(config, mode) {
18
18
  if (fs.existsSync('src/app.d.ts')) {
19
19
  const content = fs.readFileSync('src/app.d.ts', 'utf-8');
20
20
  if (content.includes('PageError')) {
21
- throw new Error(
22
- 'App.PageError has been renamed to App.Error — please update your src/app.d.ts'
23
- );
21
+ if (content.includes('// interface PageError')) {
22
+ fs.writeFileSync(
23
+ 'src/app.d.ts',
24
+ content.replace(/\/\/ interface PageError/g, '// interface Error')
25
+ );
26
+ console.warn('App.PageError has been renamed to App.Error — we updated your src/app.d.ts');
27
+ } else {
28
+ throw new Error(
29
+ 'App.PageError has been renamed to App.Error — please update your src/app.d.ts'
30
+ );
31
+ }
24
32
  }
25
33
  }
26
34
 
@@ -617,7 +617,7 @@ export function tweak_types(content, is_server) {
617
617
  }
618
618
  }
619
619
  }
620
- modified ||= _modified;
620
+ modified = modified || _modified;
621
621
  return _modified;
622
622
  }
623
623
 
@@ -25,6 +25,7 @@ export function enhance(form, submit = () => {}) {
25
25
  */
26
26
  const fallback_callback = async ({ action, result }) => {
27
27
  if (result.type === 'success') {
28
+ form.reset();
28
29
  await invalidateAll();
29
30
  }
30
31
 
@@ -1,7 +1,13 @@
1
1
  import { onMount, tick } from 'svelte';
2
2
  import { make_trackable, decode_params, normalize_path } from '../../utils/url.js';
3
3
  import { find_anchor, get_base_uri, scroll_state } from './utils.js';
4
- import { lock_fetch, unlock_fetch, initial_fetch, subsequent_fetch } from './fetcher.js';
4
+ import {
5
+ lock_fetch,
6
+ unlock_fetch,
7
+ initial_fetch,
8
+ subsequent_fetch,
9
+ native_fetch
10
+ } from './fetcher.js';
5
11
  import { parse } from './parse.js';
6
12
 
7
13
  import Root from '__GENERATED__/root.svelte';
@@ -10,6 +16,7 @@ import { HttpError, Redirect } from '../control.js';
10
16
  import { stores } from './singletons.js';
11
17
  import { DATA_SUFFIX } from '../../constants.js';
12
18
  import { unwrap_promises } from '../../utils/promises.js';
19
+ import * as devalue from 'devalue';
13
20
 
14
21
  const SCROLL_KEY = 'sveltekit:scroll';
15
22
  const INDEX_KEY = 'sveltekit:index';
@@ -1479,8 +1486,6 @@ export function create_client({ target, base, trailing_slash }) {
1479
1486
  };
1480
1487
  }
1481
1488
 
1482
- let data_id = 1;
1483
-
1484
1489
  /**
1485
1490
  * @param {URL} url
1486
1491
  * @param {boolean[]} invalid
@@ -1489,25 +1494,20 @@ let data_id = 1;
1489
1494
  async function load_data(url, invalid) {
1490
1495
  const data_url = new URL(url);
1491
1496
  data_url.pathname = url.pathname.replace(/\/$/, '') + DATA_SUFFIX;
1492
- data_url.searchParams.set('__invalid', invalid.map((x) => (x ? 'y' : 'n')).join(''));
1493
- data_url.searchParams.set('__id', String(data_id++));
1494
-
1495
- // The __data.js file is generated by the server and looks like
1496
- // `window.__sveltekit_data = ${devalue.uneval(data)}`. We do this instead
1497
- // of `export const data` because modules are cached indefinitely,
1498
- // and that would cause memory leaks.
1499
- //
1500
- // The data is read and deleted in the same tick as the promise
1501
- // resolves, so it's not vulnerable to race conditions
1502
- await import(/* @vite-ignore */ data_url.href);
1503
1497
 
1504
- // @ts-expect-error
1505
- const server_data = window.__sveltekit_data;
1498
+ const res = await native_fetch(data_url.href, {
1499
+ headers: {
1500
+ 'x-sveltekit-invalidated': invalid.map((x) => (x ? '1' : '')).join(',')
1501
+ }
1502
+ });
1503
+ const server_data = await res.text();
1506
1504
 
1507
- // @ts-expect-error
1508
- delete window.__sveltekit_data;
1505
+ if (!res.ok) {
1506
+ // error message is a JSON-stringified string which devalue can't handle at the top level
1507
+ throw new Error(JSON.parse(server_data));
1508
+ }
1509
1509
 
1510
- return server_data;
1510
+ return devalue.parse(server_data);
1511
1511
  }
1512
1512
 
1513
1513
  /**
@@ -2,7 +2,7 @@ import { hash } from '../hash.js';
2
2
 
3
3
  let loading = 0;
4
4
 
5
- const native_fetch = window.fetch;
5
+ export const native_fetch = window.fetch;
6
6
 
7
7
  export function lock_fetch() {
8
8
  loading += 1;
@@ -15,7 +15,7 @@ import { DATA_SUFFIX } from '../../../constants.js';
15
15
  */
16
16
  export async function render_data(event, route, options, state) {
17
17
  if (!route.page) {
18
- // requesting /__data.js should fail for a +server.js
18
+ // requesting /__data.json should fail for a +server.js
19
19
  return new Response(undefined, {
20
20
  status: 404
21
21
  });
@@ -25,10 +25,8 @@ export async function render_data(event, route, options, state) {
25
25
  const node_ids = [...route.page.layouts, route.page.leaf];
26
26
 
27
27
  const invalidated =
28
- event.url.searchParams
29
- .get('__invalid')
30
- ?.split('')
31
- .map((x) => x === 'y') ?? node_ids.map(() => true);
28
+ event.request.headers.get('x-sveltekit-invalidated')?.split(',').map(Boolean) ??
29
+ node_ids.map(() => true);
32
30
 
33
31
  let aborted = false;
34
32
 
@@ -38,9 +36,6 @@ export async function render_data(event, route, options, state) {
38
36
  options.trailing_slash
39
37
  );
40
38
 
41
- url.searchParams.delete('__invalid');
42
- url.searchParams.delete('__id');
43
-
44
39
  const new_event = { ...event, url };
45
40
 
46
41
  const functions = node_ids.map((n, i) => {
@@ -3,6 +3,7 @@ import { render_page } from './page/index.js';
3
3
  import { render_response } from './page/render.js';
4
4
  import { respond_with_error } from './page/respond_with_error.js';
5
5
  import { coalesce_to_error } from '../../utils/error.js';
6
+ import { is_form_content_type } from '../../utils/http.js';
6
7
  import { GENERIC_ERROR, handle_fatal_error } from './utils.js';
7
8
  import { decode_params, disable_search, normalize_path } from '../../utils/url.js';
8
9
  import { exec } from '../../utils/routing.js';
@@ -24,12 +25,10 @@ export async function respond(request, options, state) {
24
25
  let url = new URL(request.url);
25
26
 
26
27
  if (options.csrf.check_origin) {
27
- const type = request.headers.get('content-type')?.split(';')[0];
28
-
29
28
  const forbidden =
30
29
  request.method === 'POST' &&
31
30
  request.headers.get('origin') !== url.origin &&
32
- (type === 'application/x-www-form-urlencoded' || type === 'multipart/form-data');
31
+ is_form_content_type(request);
33
32
 
34
33
  if (forbidden) {
35
34
  return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
@@ -292,7 +291,7 @@ export async function respond(request, options, state) {
292
291
  // add headers/cookies here, rather than inside `resolve`, so that we
293
292
  // can do it once for all responses instead of once per `return`
294
293
  if (!is_data_request) {
295
- // we only want to set cookies on __data.js requests, we don't
294
+ // we only want to set cookies on __data.json requests, we don't
296
295
  // want to cache stuff erroneously etc
297
296
  for (const key in headers) {
298
297
  const value = headers[key];
@@ -1,6 +1,6 @@
1
1
  import { error, json } from '../../../exports/index.js';
2
2
  import { normalize_error } from '../../../utils/error.js';
3
- import { negotiate } from '../../../utils/http.js';
3
+ import { is_form_content_type, negotiate } from '../../../utils/http.js';
4
4
  import { HttpError, Redirect, ValidationError } from '../../control.js';
5
5
  import { handle_error_and_jsonify } from '../utils.js';
6
6
 
@@ -180,9 +180,10 @@ export async function call_action(event, actions) {
180
180
  throw new Error(`No action with name '${name}' found`);
181
181
  }
182
182
 
183
- const type = event.request.headers.get('content-type')?.split('; ')[0];
184
- if (type !== 'application/x-www-form-urlencoded' && type !== 'multipart/form-data') {
185
- throw new Error(`Actions expect form-encoded data (received ${type})`);
183
+ if (!is_form_content_type(event.request)) {
184
+ throw new Error(
185
+ `Actions expect form-encoded data (received ${event.request.headers.get('content-type')}`
186
+ );
186
187
  }
187
188
 
188
189
  return action(event);
@@ -205,10 +205,10 @@ export async function render_page(event, route, page, options, state, resolve_op
205
205
 
206
206
  if (err instanceof Redirect) {
207
207
  if (state.prerendering && should_prerender_data) {
208
- const body = `window.__sveltekit_data = ${JSON.stringify({
208
+ const body = devalue.stringify({
209
209
  type: 'redirect',
210
210
  location: err.location
211
- })}`;
211
+ });
212
212
 
213
213
  state.prerendering.dependencies.set(data_pathname, {
214
214
  response: new Response(body),
@@ -260,10 +260,10 @@ export async function render_page(event, route, page, options, state, resolve_op
260
260
  }
261
261
 
262
262
  if (state.prerendering && should_prerender_data) {
263
- const body = `window.__sveltekit_data = ${devalue.uneval({
263
+ const body = devalue.stringify({
264
264
  type: 'data',
265
265
  nodes: branch.map((branch_node) => branch_node?.server_data)
266
- })}`;
266
+ });
267
267
 
268
268
  state.prerendering.dependencies.set(data_pathname, {
269
269
  response: new Response(body),
@@ -70,17 +70,17 @@ export function allowed_methods(mod) {
70
70
  /** @param {any} data */
71
71
  export function data_response(data) {
72
72
  const headers = {
73
- 'content-type': 'application/javascript',
73
+ 'content-type': 'application/json',
74
74
  'cache-control': 'private, no-store'
75
75
  };
76
76
 
77
77
  try {
78
- return new Response(`window.__sveltekit_data = ${devalue.uneval(data)}`, { headers });
78
+ return new Response(devalue.stringify(data), { headers });
79
79
  } catch (e) {
80
80
  const error = /** @type {any} */ (e);
81
81
  const match = /\[(\d+)\]\.data\.(.+)/.exec(error.path);
82
82
  const message = match ? `${error.message} (data.${match[2]})` : error.message;
83
- return new Response(`throw new Error(${JSON.stringify(message)})`, { headers });
83
+ return new Response(JSON.stringify(message), { headers, status: 500 });
84
84
  }
85
85
  }
86
86
 
package/src/utils/http.js CHANGED
@@ -53,3 +53,20 @@ export function negotiate(accept, types) {
53
53
 
54
54
  return accepted;
55
55
  }
56
+
57
+ /**
58
+ * Returns `true` if the request contains a `content-type` header with the given type
59
+ * @param {Request} request
60
+ * @param {...string} types
61
+ */
62
+ export function is_content_type(request, ...types) {
63
+ const type = request.headers.get('content-type')?.split(';', 1)[0].trim() ?? '';
64
+ return types.includes(type);
65
+ }
66
+
67
+ /**
68
+ * @param {Request} request
69
+ */
70
+ export function is_form_content_type(request) {
71
+ return is_content_type(request, 'application/x-www-form-urlencoded', 'multipart/form-data');
72
+ }