@sveltejs/kit 1.0.0-next.526 → 1.0.0-next.528

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.526",
3
+ "version": "1.0.0-next.528",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
package/src/constants.js CHANGED
@@ -3,5 +3,3 @@
3
3
  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
-
7
- export const DATA_SUFFIX = '/__data.json';
@@ -291,6 +291,8 @@ export async function dev(vite, vite_config, svelte_config) {
291
291
  remove_static_middlewares(vite.middlewares);
292
292
 
293
293
  vite.middlewares.use(async (req, res) => {
294
+ // Vite's base middleware strips out the base path. Restore it
295
+ req.url = req.originalUrl;
294
296
  try {
295
297
  const base = `${vite.config.server.https ? 'https' : 'http'}://${
296
298
  req.headers[':authority'] || req.headers.host
@@ -1,5 +1,5 @@
1
1
  import { onMount, tick } from 'svelte';
2
- import { make_trackable, decode_params, normalize_path } from '../../utils/url.js';
2
+ import { make_trackable, decode_params, normalize_path, add_data_suffix } from '../../utils/url.js';
3
3
  import { find_anchor, get_base_uri, scroll_state } from './utils.js';
4
4
  import {
5
5
  lock_fetch,
@@ -14,7 +14,6 @@ import Root from '__GENERATED__/root.svelte';
14
14
  import { nodes, server_loads, dictionary, matchers, hooks } from '__GENERATED__/client-manifest.js';
15
15
  import { HttpError, Redirect } from '../control.js';
16
16
  import { stores } from './singletons.js';
17
- import { DATA_SUFFIX } from '../../constants.js';
18
17
  import { unwrap_promises } from '../../utils/promises.js';
19
18
  import * as devalue from 'devalue';
20
19
 
@@ -164,13 +163,19 @@ export function create_client({ target, base, trailing_slash }) {
164
163
 
165
164
  /**
166
165
  * @param {string | URL} url
167
- * @param {{ noscroll?: boolean; replaceState?: boolean; keepfocus?: boolean; state?: any }} opts
166
+ * @param {{ noscroll?: boolean; replaceState?: boolean; keepfocus?: boolean; state?: any; invalidateAll?: boolean }} opts
168
167
  * @param {string[]} redirect_chain
169
168
  * @param {{}} [nav_token]
170
169
  */
171
170
  async function goto(
172
171
  url,
173
- { noscroll = false, replaceState = false, keepfocus = false, state = {} },
172
+ {
173
+ noscroll = false,
174
+ replaceState = false,
175
+ keepfocus = false,
176
+ state = {},
177
+ invalidateAll = false
178
+ },
174
179
  redirect_chain,
175
180
  nav_token
176
181
  ) {
@@ -188,7 +193,11 @@ export function create_client({ target, base, trailing_slash }) {
188
193
  replaceState
189
194
  },
190
195
  nav_token,
191
- accepted: () => {},
196
+ accepted: () => {
197
+ if (invalidateAll) {
198
+ force_invalidation = true;
199
+ }
200
+ },
192
201
  blocked: () => {},
193
202
  type: 'goto'
194
203
  });
@@ -1212,7 +1221,7 @@ export function create_client({ target, base, trailing_slash }) {
1212
1221
  post_update();
1213
1222
  }
1214
1223
  } else if (result.type === 'redirect') {
1215
- goto(result.location, {}, []);
1224
+ goto(result.location, { invalidateAll: true }, []);
1216
1225
  } else {
1217
1226
  /** @type {Record<string, any>} */
1218
1227
  const props = {
@@ -1501,7 +1510,7 @@ export function create_client({ target, base, trailing_slash }) {
1501
1510
  */
1502
1511
  async function load_data(url, invalid) {
1503
1512
  const data_url = new URL(url);
1504
- data_url.pathname = url.pathname.replace(/\/$/, '') + DATA_SUFFIX;
1513
+ data_url.pathname = add_data_suffix(url.pathname);
1505
1514
 
1506
1515
  const res = await native_fetch(data_url.href, {
1507
1516
  headers: {
@@ -1,10 +1,19 @@
1
1
  import { parse, serialize } from 'cookie';
2
+ import { has_data_suffix, normalize_path, strip_data_suffix } from '../../utils/url.js';
3
+
4
+ /**
5
+ * Tracks all cookies set during dev mode so we can emit warnings
6
+ * when we detect that there's likely cookie misusage due to wrong paths
7
+ *
8
+ * @type {Record<string, Set<string>>} */
9
+ const cookie_paths = {};
2
10
 
3
11
  /**
4
12
  * @param {Request} request
5
13
  * @param {URL} url
14
+ * @param {Pick<import('types').SSROptions, 'dev' | 'trailing_slash'>} options
6
15
  */
7
- export function get_cookies(request, url) {
16
+ export function get_cookies(request, url, options) {
8
17
  const header = request.headers.get('cookie') ?? '';
9
18
 
10
19
  const initial_cookies = parse(header);
@@ -42,7 +51,17 @@ export function get_cookies(request, url) {
42
51
 
43
52
  const decode = opts?.decode || decodeURIComponent;
44
53
  const req_cookies = parse(header, { decode });
45
- return req_cookies[name]; // the decoded string or undefined
54
+ const cookie = req_cookies[name]; // the decoded string or undefined
55
+
56
+ if (!options.dev || cookie) {
57
+ return cookie;
58
+ }
59
+
60
+ if (c || cookie_paths[name]?.size > 0) {
61
+ console.warn(
62
+ `Cookie with name '${name}' was not found, but a cookie with that name exists at a sub path. Did you mean to set its 'path' to '/'?`
63
+ );
64
+ }
46
65
  },
47
66
 
48
67
  /**
@@ -51,14 +70,43 @@ export function get_cookies(request, url) {
51
70
  * @param {import('cookie').CookieSerializeOptions} opts
52
71
  */
53
72
  set(name, value, opts = {}) {
73
+ let path = opts.path;
74
+ if (!path) {
75
+ const normalized = normalize_path(
76
+ // Remove suffix: 'foo/__data.json' would mean the cookie path is '/foo',
77
+ // whereas a direct hit of /foo would mean the cookie path is '/'
78
+ has_data_suffix(url.pathname) ? strip_data_suffix(url.pathname) : url.pathname,
79
+ options.trailing_slash
80
+ );
81
+ // Emulate browser-behavior: if the cookie is set at '/foo/bar', its path is '/foo'
82
+ path = normalized.split('/').slice(0, -1).join('/') || '/';
83
+ }
84
+
54
85
  new_cookies[name] = {
55
86
  name,
56
87
  value,
57
88
  options: {
58
89
  ...defaults,
59
- ...opts
90
+ ...opts,
91
+ path
60
92
  }
61
93
  };
94
+
95
+ if (options.dev) {
96
+ cookie_paths[name] = cookie_paths[name] || new Set();
97
+ if (!value) {
98
+ if (!cookie_paths[name].has(path) && cookie_paths[name].size > 0) {
99
+ console.warn(
100
+ `Trying to delete cookie '${name}' at path '${path}', but a cookie with that name only exists at a different path.`
101
+ );
102
+ }
103
+ cookie_paths[name].delete(path);
104
+ } else {
105
+ // We could also emit a warning here if the cookie already exists at a different path,
106
+ // but that's more likely a false positive because it's valid to set the same name at different paths
107
+ cookie_paths[name].add(path);
108
+ }
109
+ }
62
110
  },
63
111
 
64
112
  /**
@@ -66,15 +114,10 @@ export function get_cookies(request, url) {
66
114
  * @param {import('cookie').CookieSerializeOptions} opts
67
115
  */
68
116
  delete(name, opts = {}) {
69
- new_cookies[name] = {
70
- name,
71
- value: '',
72
- options: {
73
- ...defaults,
74
- ...opts,
75
- maxAge: 0
76
- }
77
- };
117
+ cookies.set(name, '', {
118
+ ...opts,
119
+ maxAge: 0
120
+ });
78
121
  },
79
122
 
80
123
  /**
@@ -3,8 +3,7 @@ import { normalize_error } from '../../../utils/error.js';
3
3
  import { once } from '../../../utils/functions.js';
4
4
  import { load_server_data } from '../page/load_data.js';
5
5
  import { data_response, handle_error_and_jsonify } from '../utils.js';
6
- import { normalize_path } from '../../../utils/url.js';
7
- import { DATA_SUFFIX } from '../../../constants.js';
6
+ import { normalize_path, strip_data_suffix } from '../../../utils/url.js';
8
7
 
9
8
  /**
10
9
  * @param {import('types').RequestEvent} event
@@ -31,10 +30,7 @@ export async function render_data(event, route, options, state) {
31
30
  let aborted = false;
32
31
 
33
32
  const url = new URL(event.url);
34
- url.pathname = normalize_path(
35
- url.pathname.slice(0, -DATA_SUFFIX.length),
36
- options.trailing_slash
37
- );
33
+ url.pathname = normalize_path(strip_data_suffix(url.pathname), options.trailing_slash);
38
34
 
39
35
  const new_event = { ...event, url };
40
36
 
@@ -5,10 +5,15 @@ import { respond_with_error } from './page/respond_with_error.js';
5
5
  import { coalesce_to_error } from '../../utils/error.js';
6
6
  import { is_form_content_type } from '../../utils/http.js';
7
7
  import { GENERIC_ERROR, handle_fatal_error } from './utils.js';
8
- import { decode_params, disable_search, normalize_path } from '../../utils/url.js';
8
+ import {
9
+ decode_params,
10
+ disable_search,
11
+ has_data_suffix,
12
+ normalize_path,
13
+ strip_data_suffix
14
+ } from '../../utils/url.js';
9
15
  import { exec } from '../../utils/routing.js';
10
16
  import { render_data } from './data/index.js';
11
- import { DATA_SUFFIX } from '../../constants.js';
12
17
  import { add_cookies_to_headers, get_cookies } from './cookie.js';
13
18
  import { HttpError } from '../control.js';
14
19
  import { create_fetch } from './fetch.js';
@@ -57,8 +62,8 @@ export async function respond(request, options, state) {
57
62
  decoded = decoded.slice(options.paths.base.length) || '/';
58
63
  }
59
64
 
60
- const is_data_request = decoded.endsWith(DATA_SUFFIX);
61
- if (is_data_request) decoded = decoded.slice(0, -DATA_SUFFIX.length) || '/';
65
+ const is_data_request = has_data_suffix(decoded);
66
+ if (is_data_request) decoded = strip_data_suffix(decoded);
62
67
 
63
68
  if (!state.prerendering?.fallback) {
64
69
  const matchers = await options.manifest._.matchers();
@@ -96,7 +101,7 @@ export async function respond(request, options, state) {
96
101
  /** @type {Record<string, string>} */
97
102
  const headers = {};
98
103
 
99
- const { cookies, new_cookies, get_cookie_header } = get_cookies(request, url);
104
+ const { cookies, new_cookies, get_cookie_header } = get_cookies(request, url, options);
100
105
 
101
106
  if (state.prerendering) disable_search(url);
102
107
 
@@ -1,7 +1,7 @@
1
1
  import * as devalue from 'devalue';
2
- import { DATA_SUFFIX } from '../../../constants.js';
3
2
  import { compact } from '../../../utils/array.js';
4
3
  import { normalize_error } from '../../../utils/error.js';
4
+ import { add_data_suffix } from '../../../utils/url.js';
5
5
  import { HttpError, Redirect } from '../../control.js';
6
6
  import {
7
7
  get_option,
@@ -76,7 +76,7 @@ export async function render_page(event, route, page, options, state, resolve_op
76
76
  }
77
77
 
78
78
  const should_prerender_data = nodes.some((node) => node?.server);
79
- const data_pathname = event.url.pathname.replace(/\/$/, '') + DATA_SUFFIX;
79
+ const data_pathname = add_data_suffix(event.url.pathname);
80
80
 
81
81
  // it's crucial that we do this before returning the non-SSR response, otherwise
82
82
  // SvelteKit will erroneously believe that the path has been prerendered,
@@ -1,6 +1,6 @@
1
1
  import * as devalue from 'devalue';
2
- import { DATA_SUFFIX } from '../../constants.js';
3
2
  import { negotiate } from '../../utils/http.js';
3
+ import { has_data_suffix } from '../../utils/url.js';
4
4
  import { HttpError } from '../control.js';
5
5
 
6
6
  /** @param {any} body */
@@ -144,7 +144,7 @@ export function handle_fatal_error(event, options, error) {
144
144
  'text/html'
145
145
  ]);
146
146
 
147
- if (event.url.pathname.endsWith(DATA_SUFFIX) || type === 'application/json') {
147
+ if (has_data_suffix(event.url.pathname) || type === 'application/json') {
148
148
  return new Response(JSON.stringify(body), {
149
149
  status,
150
150
  headers: { 'content-type': 'application/json; charset=utf-8' }
package/src/utils/url.js CHANGED
@@ -142,3 +142,20 @@ export function disable_search(url) {
142
142
  });
143
143
  }
144
144
  }
145
+
146
+ const DATA_SUFFIX = '/__data.json';
147
+
148
+ /** @param {string} pathname */
149
+ export function has_data_suffix(pathname) {
150
+ return pathname.endsWith(DATA_SUFFIX);
151
+ }
152
+
153
+ /** @param {string} pathname */
154
+ export function add_data_suffix(pathname) {
155
+ return pathname.replace(/\/$/, '') + DATA_SUFFIX;
156
+ }
157
+
158
+ /** @param {string} pathname */
159
+ export function strip_data_suffix(pathname) {
160
+ return pathname.slice(0, -DATA_SUFFIX.length);
161
+ }
@@ -182,14 +182,32 @@ declare module '$app/navigation' {
182
182
  * Returns a Promise that resolves when SvelteKit navigates (or fails to navigate, in which case the promise rejects) to the specified `url`.
183
183
  *
184
184
  * @param url Where to navigate to. Note that if you've set [`config.kit.paths.base`](https://kit.svelte.dev/docs/configuration#paths) and the URL is root-relative, you need to prepend the base path if you want to navigate within the app.
185
- * @param opts.replaceState If `true`, will replace the current `history` entry rather than creating a new one with `pushState`
186
- * @param opts.noscroll If `true`, the browser will maintain its scroll position rather than scrolling to the top of the page after navigation
187
- * @param opts.keepfocus If `true`, the currently focused element will retain focus after navigation. Otherwise, focus will be reset to the body
188
- * @param opts.state The state of the new/updated history entry
185
+ * @param opts Options related to the navigation
189
186
  */
190
187
  export function goto(
191
188
  url: string | URL,
192
- opts?: { replaceState?: boolean; noscroll?: boolean; keepfocus?: boolean; state?: any }
189
+ opts?: {
190
+ /**
191
+ * If `true`, will replace the current `history` entry rather than creating a new one with `pushState`
192
+ */
193
+ replaceState?: boolean;
194
+ /**
195
+ * If `true`, the browser will maintain its scroll position rather than scrolling to the top of the page after navigation
196
+ */
197
+ noscroll?: boolean;
198
+ /**
199
+ * If `true`, the currently focused element will retain focus after navigation. Otherwise, focus will be reset to the body
200
+ */
201
+ keepfocus?: boolean;
202
+ /**
203
+ * The state of the new/updated history entry
204
+ */
205
+ state?: any;
206
+ /**
207
+ * If `true`, all `load` functions of the page will be rerun. See https://kit.svelte.dev/docs/load#invalidation for more info on invalidation.
208
+ */
209
+ invalidateAll?: boolean;
210
+ }
193
211
  ): Promise<void>;
194
212
  /**
195
213
  * Causes any `load` functions belonging to the currently active page to re-run if they depend on the `url` in question, via `fetch` or `depends`. Returns a `Promise` that resolves when the page is subsequently updated.
package/types/index.d.ts CHANGED
@@ -161,6 +161,8 @@ export interface Cookies {
161
161
 
162
162
  /**
163
163
  * Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.
164
+ *
165
+ * By default, the `path` of a cookie is the 'directory' of the current pathname. In most cases you should explicitly set `path: '/'` to make the cookie available throughout your app.
164
166
  */
165
167
  delete(name: string, opts?: import('cookie').CookieSerializeOptions): void;
166
168