@sveltejs/kit 2.15.3 → 2.16.0

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.
@@ -8,13 +8,7 @@ import {
8
8
  make_trackable,
9
9
  normalize_path
10
10
  } from '../../utils/url.js';
11
- import {
12
- initial_fetch,
13
- lock_fetch,
14
- native_fetch,
15
- subsequent_fetch,
16
- unlock_fetch
17
- } from './fetcher.js';
11
+ import { dev_fetch, initial_fetch, lock_fetch, subsequent_fetch, unlock_fetch } from './fetcher.js';
18
12
  import { parse } from './parse.js';
19
13
  import * as storage from './session-storage.js';
20
14
  import {
@@ -312,7 +306,9 @@ export async function start(_app, _target, hydrate) {
312
306
  if (hydrate) {
313
307
  await _hydrate(target, hydrate);
314
308
  } else {
315
- goto(location.href, { replaceState: true });
309
+ goto(app.hash ? decode_hash(new URL(location.href)) : location.href, {
310
+ replaceState: true
311
+ });
316
312
  }
317
313
 
318
314
  _start_router();
@@ -380,7 +376,7 @@ function persist_state() {
380
376
 
381
377
  /**
382
378
  * @param {string | URL} url
383
- * @param {{ replaceState?: boolean; noScroll?: boolean; keepFocus?: boolean; invalidateAll?: boolean; state?: Record<string, any> }} options
379
+ * @param {{ replaceState?: boolean; noScroll?: boolean; keepFocus?: boolean; invalidateAll?: boolean; invalidate?: Array<string | URL | ((url: URL) => boolean)>; state?: Record<string, any> }} options
384
380
  * @param {number} redirect_count
385
381
  * @param {{}} [nav_token]
386
382
  */
@@ -398,6 +394,10 @@ async function _goto(url, options, redirect_count, nav_token) {
398
394
  if (options.invalidateAll) {
399
395
  force_invalidation = true;
400
396
  }
397
+
398
+ if (options.invalidate) {
399
+ options.invalidate.forEach(push_invalidated);
400
+ }
401
401
  }
402
402
  });
403
403
  }
@@ -428,9 +428,15 @@ async function _preload_data(intent) {
428
428
  return load_cache.promise;
429
429
  }
430
430
 
431
- /** @param {URL} url */
431
+ /**
432
+ * @param {URL} url
433
+ * @returns {Promise<void>}
434
+ */
432
435
  async function _preload_code(url) {
433
- const route = routes.find((route) => route.exec(get_url_path(url)));
436
+ const rerouted = get_rerouted_url(url);
437
+ if (!rerouted) return;
438
+
439
+ const route = routes.find((route) => route.exec(get_url_path(rerouted)));
434
440
 
435
441
  if (route) {
436
442
  await Promise.all([...route.layouts, route.leaf].map((load) => load?.[1]()));
@@ -1191,16 +1197,11 @@ async function load_root_error_page({ status, error, url, route }) {
1191
1197
  }
1192
1198
 
1193
1199
  /**
1194
- * Resolve the full info (which route, params, etc.) for a client-side navigation from the URL,
1195
- * taking the reroute hook into account. If this isn't a client-side-navigation (or the URL is undefined),
1196
- * returns undefined.
1197
- * @param {URL | undefined} url
1198
- * @param {boolean} invalidating
1200
+ * Resolve the relative rerouted URL for a client-side navigation
1201
+ * @param {URL} url
1202
+ * @returns {URL | undefined}
1199
1203
  */
1200
- function get_navigation_intent(url, invalidating) {
1201
- if (!url) return;
1202
- if (is_external_url(url, base, app.hash)) return;
1203
-
1204
+ function get_rerouted_url(url) {
1204
1205
  // reroute could alter the given URL, so we pass a copy
1205
1206
  let rerouted;
1206
1207
  try {
@@ -1227,9 +1228,26 @@ function get_navigation_intent(url, invalidating) {
1227
1228
  }
1228
1229
 
1229
1230
  // fall back to native navigation
1230
- return undefined;
1231
+ return;
1231
1232
  }
1232
1233
 
1234
+ return rerouted;
1235
+ }
1236
+
1237
+ /**
1238
+ * Resolve the full info (which route, params, etc.) for a client-side navigation from the URL,
1239
+ * taking the reroute hook into account. If this isn't a client-side-navigation (or the URL is undefined),
1240
+ * returns undefined.
1241
+ * @param {URL | undefined} url
1242
+ * @param {boolean} invalidating
1243
+ */
1244
+ function get_navigation_intent(url, invalidating) {
1245
+ if (!url) return;
1246
+ if (is_external_url(url, base, app.hash)) return;
1247
+
1248
+ const rerouted = get_rerouted_url(url);
1249
+ if (!rerouted) return;
1250
+
1233
1251
  const path = get_url_path(rerouted);
1234
1252
 
1235
1253
  for (const route of routes) {
@@ -1811,6 +1829,7 @@ export function disableScrollHandling() {
1811
1829
  * @param {boolean} [opts.noScroll] If `true`, the browser will maintain its scroll position rather than scrolling to the top of the page after navigation
1812
1830
  * @param {boolean} [opts.keepFocus] If `true`, the currently focused element will retain focus after navigation. Otherwise, focus will be reset to the body
1813
1831
  * @param {boolean} [opts.invalidateAll] If `true`, all `load` functions of the page will be rerun. See https://svelte.dev/docs/kit/load#rerunning-load-functions for more info on invalidation.
1832
+ * @param {Array<string | URL | ((url: URL) => boolean)>} [opts.invalidate] Causes any load functions to re-run if they depend on one of the urls
1814
1833
  * @param {App.PageState} [opts.state] An optional object that will be available as `page.state`
1815
1834
  * @returns {Promise<void>}
1816
1835
  */
@@ -1857,14 +1876,21 @@ export function invalidate(resource) {
1857
1876
  throw new Error('Cannot call invalidate(...) on the server');
1858
1877
  }
1859
1878
 
1879
+ push_invalidated(resource);
1880
+
1881
+ return _invalidate();
1882
+ }
1883
+
1884
+ /**
1885
+ * @param {string | URL | ((url: URL) => boolean)} resource The invalidated URL
1886
+ */
1887
+ function push_invalidated(resource) {
1860
1888
  if (typeof resource === 'function') {
1861
1889
  invalidated.push(resource);
1862
1890
  } else {
1863
1891
  const { href } = new URL(resource, location.href);
1864
1892
  invalidated.push((url) => url.href === href);
1865
1893
  }
1866
-
1867
- return _invalidate();
1868
1894
  }
1869
1895
 
1870
1896
  /**
@@ -1936,13 +1962,20 @@ export function preloadCode(pathname) {
1936
1962
  const url = new URL(pathname, current.url);
1937
1963
 
1938
1964
  if (DEV) {
1965
+ if (!pathname.startsWith('/')) {
1966
+ throw new Error(
1967
+ 'argument passed to preloadCode must be a pathname (i.e. "/about" rather than "http://example.com/about"'
1968
+ );
1969
+ }
1970
+
1939
1971
  if (!pathname.startsWith(base)) {
1940
1972
  throw new Error(
1941
- `pathnames passed to preloadCode must start with \`paths.base\` (i.e. "${base}${pathname}" rather than "${pathname}")`
1973
+ `pathname passed to preloadCode must start with \`paths.base\` (i.e. "${base}${pathname}" rather than "${pathname}")`
1942
1974
  );
1943
1975
  }
1944
1976
 
1945
- if (!routes.find((route) => route.exec(get_url_path(url)))) {
1977
+ const rerouted = get_rerouted_url(url);
1978
+ if (!rerouted || !routes.find((route) => route.exec(get_url_path(rerouted)))) {
1946
1979
  throw new Error(`'${pathname}' did not match any routes`);
1947
1980
  }
1948
1981
  }
@@ -2401,7 +2434,7 @@ function _start_router() {
2401
2434
  // (surprisingly!) mutates `current.url`, allowing us to
2402
2435
  // detect it and trigger a navigation
2403
2436
  if (current.url.hash === location.hash) {
2404
- navigate({ type: 'goto', url: current.url });
2437
+ navigate({ type: 'goto', url: decode_hash(current.url) });
2405
2438
  }
2406
2439
  }
2407
2440
  });
@@ -2548,7 +2581,9 @@ async function load_data(url, invalid) {
2548
2581
  }
2549
2582
  data_url.searchParams.append(INVALIDATED_PARAM, invalid.map((i) => (i ? '1' : '0')).join(''));
2550
2583
 
2551
- const res = await native_fetch(data_url.href);
2584
+ // use window.fetch directly to allow using a 3rd party-patched fetch implementation
2585
+ const fetcher = DEV ? dev_fetch : window.fetch;
2586
+ const res = await fetcher(data_url.href, {});
2552
2587
 
2553
2588
  if (!res.ok) {
2554
2589
  // error message is a JSON-stringified string which devalue can't handle at the top level
@@ -2793,6 +2828,17 @@ function clone_page(page) {
2793
2828
  };
2794
2829
  }
2795
2830
 
2831
+ /**
2832
+ * @param {URL} url
2833
+ * @returns {URL}
2834
+ */
2835
+ function decode_hash(url) {
2836
+ const new_url = new URL(url);
2837
+ // Safari, for some reason, does change # to %23, when entered through the address bar
2838
+ new_url.hash = decodeURIComponent(url.hash);
2839
+ return new_url;
2840
+ }
2841
+
2796
2842
  if (DEV) {
2797
2843
  // Nasty hack to silence harmless warnings the user can do nothing about
2798
2844
  const console_warn = console.warn;
@@ -5,7 +5,7 @@ import { b64_decode } from '../utils.js';
5
5
  let loading = 0;
6
6
 
7
7
  /** @type {typeof fetch} */
8
- export const native_fetch = BROWSER ? window.fetch : /** @type {any} */ (() => {});
8
+ const native_fetch = BROWSER ? window.fetch : /** @type {any} */ (() => {});
9
9
 
10
10
  export function lock_fetch() {
11
11
  loading += 1;
@@ -137,7 +137,7 @@ export function subsequent_fetch(resource, resolved, opts) {
137
137
  * @param {RequestInfo | URL} resource
138
138
  * @param {RequestInit & Record<string, any> | undefined} opts
139
139
  */
140
- function dev_fetch(resource, opts) {
140
+ export function dev_fetch(resource, opts) {
141
141
  const patched_opts = { ...opts };
142
142
  // This assigns the __sveltekit_fetch__ flag and makes it non-enumerable
143
143
  Object.defineProperty(patched_opts, '__sveltekit_fetch__', {
@@ -311,7 +311,7 @@ export function is_external_url(url, base, hash_routing) {
311
311
  }
312
312
 
313
313
  if (hash_routing) {
314
- if (url.pathname === base + '/') {
314
+ if (url.pathname === base + '/' || url.pathname === base + '/index.html') {
315
315
  return false;
316
316
  }
317
317
 
@@ -55,7 +55,7 @@ export function get_cookies(request, url, trailing_slash) {
55
55
 
56
56
  /**
57
57
  * @param {string} name
58
- * @param {import('cookie').CookieParseOptions} opts
58
+ * @param {import('cookie').CookieParseOptions} [opts]
59
59
  */
60
60
  get(name, opts) {
61
61
  const c = new_cookies[name];
@@ -91,7 +91,7 @@ export function get_cookies(request, url, trailing_slash) {
91
91
  },
92
92
 
93
93
  /**
94
- * @param {import('cookie').CookieParseOptions} opts
94
+ * @param {import('cookie').CookieParseOptions} [opts]
95
95
  */
96
96
  getAll(opts) {
97
97
  const cookies = parse(header, { decode: opts?.decode });
@@ -1,3 +1,4 @@
1
+ import { DEV } from 'esm-env';
1
2
  import { ENDPOINT_METHODS, PAGE_METHODS } from '../../constants.js';
2
3
  import { negotiate } from '../../utils/http.js';
3
4
  import { Redirect } from '../control.js';
@@ -10,6 +11,10 @@ import { method_not_allowed } from './utils.js';
10
11
  * @returns {Promise<Response>}
11
12
  */
12
13
  export async function render_endpoint(event, mod, state) {
14
+ if (DEV && event.request.headers.get('x-sveltekit-action') === 'true') {
15
+ throw new Error('use:enhance should only be used with SvelteKit form actions');
16
+ }
17
+
13
18
  const method = /** @type {import('types').HttpMethod} */ (event.request.method);
14
19
 
15
20
  let handler = mod[method] || mod.fallback;
@@ -85,6 +85,21 @@ export async function respond(request, options, manifest, state) {
85
85
  return text('Not found', { status: 404 });
86
86
  }
87
87
 
88
+ const is_data_request = has_data_suffix(url.pathname);
89
+ /** @type {boolean[] | undefined} */
90
+ let invalidated_data_nodes;
91
+ if (is_data_request) {
92
+ url.pathname =
93
+ strip_data_suffix(url.pathname) +
94
+ (url.searchParams.get(TRAILING_SLASH_PARAM) === '1' ? '/' : '') || '/';
95
+ url.searchParams.delete(TRAILING_SLASH_PARAM);
96
+ invalidated_data_nodes = url.searchParams
97
+ .get(INVALIDATED_PARAM)
98
+ ?.split('')
99
+ .map((node) => node === '1');
100
+ url.searchParams.delete(INVALIDATED_PARAM);
101
+ }
102
+
88
103
  // reroute could alter the given URL, so we pass a copy
89
104
  let rerouted_path;
90
105
  try {
@@ -126,22 +141,6 @@ export async function respond(request, options, manifest, state) {
126
141
  return text('Not found', { status: 404, headers });
127
142
  }
128
143
 
129
- const is_data_request = has_data_suffix(decoded);
130
- /** @type {boolean[] | undefined} */
131
- let invalidated_data_nodes;
132
- if (is_data_request) {
133
- decoded = strip_data_suffix(decoded) || '/';
134
- url.pathname =
135
- strip_data_suffix(url.pathname) +
136
- (url.searchParams.get(TRAILING_SLASH_PARAM) === '1' ? '/' : '') || '/';
137
- url.searchParams.delete(TRAILING_SLASH_PARAM);
138
- invalidated_data_nodes = url.searchParams
139
- .get(INVALIDATED_PARAM)
140
- ?.split('')
141
- .map((node) => node === '1');
142
- url.searchParams.delete(INVALIDATED_PARAM);
143
- }
144
-
145
144
  if (!state.prerendering?.fallback) {
146
145
  // TODO this could theoretically break — should probably be inside a try-catch
147
146
  const matchers = await manifest._.matchers();
@@ -4,17 +4,22 @@ import { pathToFileURL } from 'node:url';
4
4
 
5
5
  /**
6
6
  * Resolve a dependency relative to the current working directory,
7
- * rather than relative to this package
7
+ * rather than relative to this package (but falls back to trying that, if necessary)
8
8
  * @param {string} dependency
9
9
  */
10
- export function resolve_peer_dependency(dependency) {
10
+ export async function resolve_peer_dependency(dependency) {
11
11
  try {
12
12
  // @ts-expect-error the types are wrong
13
13
  const resolved = imr.resolve(dependency, pathToFileURL(process.cwd() + '/dummy.js'));
14
- return import(resolved);
14
+ return await import(resolved);
15
15
  } catch {
16
- throw new Error(
17
- `Could not resolve peer dependency "${dependency}" relative to your project please install it and try again.`
18
- );
16
+ try {
17
+ // both imr.resolve and await import above can throw, which is why we can't just do import(resolved).catch(...) above
18
+ return await import(dependency);
19
+ } catch {
20
+ throw new Error(
21
+ `Could not resolve peer dependency "${dependency}" relative to your project — please install it and try again.`
22
+ );
23
+ }
19
24
  }
20
25
  }
package/src/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // generated during release, do not modify
2
2
 
3
3
  /** @type {string} */
4
- export const VERSION = '2.15.3';
4
+ export const VERSION = '2.16.0';