@sveltejs/kit 2.17.2 → 2.18.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "2.17.2",
3
+ "version": "2.18.0",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
@@ -36,7 +36,7 @@
36
36
  "@types/connect": "^3.4.38",
37
37
  "@types/node": "^18.19.48",
38
38
  "@types/set-cookie-parser": "^2.4.7",
39
- "dts-buddy": "^0.5.4",
39
+ "dts-buddy": "^0.5.5",
40
40
  "rollup": "^4.14.2",
41
41
  "svelte": "^5.2.9",
42
42
  "svelte-preprocess": "^6.0.0",
@@ -41,6 +41,7 @@ export function find_server_assets(build_data, routes) {
41
41
 
42
42
  for (const n of used_nodes) {
43
43
  const node = build_data.manifest_data.nodes[n];
44
+ if (node?.universal) add_assets(node.universal);
44
45
  if (node?.server) add_assets(node.server);
45
46
  }
46
47
 
@@ -27,12 +27,10 @@ export function generate_manifest({ build_data, prerendered, relative_path, rout
27
27
  const reindexed = new Map();
28
28
  /**
29
29
  * All nodes actually used in the routes definition (prerendered routes are omitted).
30
- * If `routes` is empty, it means that this manifest is only used for server-side resolution
31
- * and the root layout/error is therefore not needed.
32
- * Else, root layout/error is always included as they are needed for 404 and root errors.
30
+ * Root layout/error is always included as they are needed for 404 and root errors.
33
31
  * @type {Set<any>}
34
32
  */
35
- const used_nodes = new Set(routes.length > 0 ? [0, 1] : []);
33
+ const used_nodes = new Set([0, 1]);
36
34
 
37
35
  const server_assets = find_server_assets(build_data, routes);
38
36
 
@@ -1,5 +1,13 @@
1
1
  import { HttpError, Redirect, ActionFailure } from '../runtime/control.js';
2
2
  import { BROWSER, DEV } from 'esm-env';
3
+ import {
4
+ add_data_suffix,
5
+ add_resolution_suffix,
6
+ has_data_suffix,
7
+ has_resolution_suffix,
8
+ strip_data_suffix,
9
+ strip_resolution_suffix
10
+ } from '../runtime/pathname.js';
3
11
 
4
12
  export { VERSION } from '../version.js';
5
13
 
@@ -207,3 +215,49 @@ export function fail(status, data) {
207
215
  export function isActionFailure(e) {
208
216
  return e instanceof ActionFailure;
209
217
  }
218
+
219
+ /**
220
+ * Strips possible SvelteKit-internal suffixes and trailing slashes from the URL pathname.
221
+ * Returns the normalized URL as well as a method for adding the potential suffix back
222
+ * based on a new pathname (possibly including search) or URL.
223
+ * ```js
224
+ * import { normalizeUrl } from '@sveltejs/kit';
225
+ *
226
+ * const { url, denormalize } = normalizeUrl('/blog/post/__data.json');
227
+ * console.log(url.pathname); // /blog/post
228
+ * console.log(denormalize('/blog/post/a')); // /blog/post/a/__data.json
229
+ * ```
230
+ * @param {URL | string} url
231
+ * @returns {{ url: URL, wasNormalized: boolean, denormalize: (url?: string | URL) => URL }}
232
+ */
233
+ export function normalizeUrl(url) {
234
+ url = new URL(url, 'http://internal');
235
+
236
+ const is_route_resolution = has_resolution_suffix(url.pathname);
237
+ const is_data_request = has_data_suffix(url.pathname);
238
+ const has_trailing_slash = url.pathname !== '/' && url.pathname.endsWith('/');
239
+
240
+ if (is_route_resolution) {
241
+ url.pathname = strip_resolution_suffix(url.pathname);
242
+ } else if (is_data_request) {
243
+ url.pathname = strip_data_suffix(url.pathname);
244
+ } else if (has_trailing_slash) {
245
+ url.pathname = url.pathname.slice(0, -1);
246
+ }
247
+
248
+ return {
249
+ url,
250
+ wasNormalized: is_data_request || is_route_resolution || has_trailing_slash,
251
+ denormalize: (new_url = url) => {
252
+ new_url = new URL(new_url, url);
253
+ if (is_route_resolution) {
254
+ new_url.pathname = add_resolution_suffix(new_url.pathname);
255
+ } else if (is_data_request) {
256
+ new_url.pathname = add_data_suffix(new_url.pathname);
257
+ } else if (has_trailing_slash && !new_url.pathname.endsWith('/')) {
258
+ new_url.pathname += '/';
259
+ }
260
+ return new_url;
261
+ }
262
+ };
263
+ }
@@ -814,7 +814,7 @@ export type ClientInit = () => MaybePromise<void>;
814
814
  * The [`reroute`](https://svelte.dev/docs/kit/hooks#Universal-hooks-reroute) hook allows you to modify the URL before it is used to determine which route to render.
815
815
  * @since 2.3.0
816
816
  */
817
- export type Reroute = (event: { url: URL }) => void | string;
817
+ export type Reroute = (event: { url: URL }) => MaybePromise<void | string>;
818
818
 
819
819
  /**
820
820
  * The [`transport`](https://svelte.dev/docs/kit/hooks#Universal-hooks-transport) hook allows you to transport custom types across the server/client boundary.
@@ -1177,7 +1177,7 @@ export interface RequestEvent<
1177
1177
  * - During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://svelte.dev/docs/kit/hooks#Server-hooks-handle)
1178
1178
  * - During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.
1179
1179
  *
1180
- * You can learn more about making credentialed requests with cookies [here](https://svelte.dev/docs/kit/load#Cookies)
1180
+ * You can learn more about making credentialed requests with cookies [here](https://svelte.dev/docs/kit/load#Cookies).
1181
1181
  */
1182
1182
  fetch: typeof fetch;
1183
1183
  /**
@@ -1189,7 +1189,7 @@ export interface RequestEvent<
1189
1189
  */
1190
1190
  locals: App.Locals;
1191
1191
  /**
1192
- * The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object
1192
+ * The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object.
1193
1193
  */
1194
1194
  params: Params;
1195
1195
  /**
@@ -1197,15 +1197,15 @@ export interface RequestEvent<
1197
1197
  */
1198
1198
  platform: Readonly<App.Platform> | undefined;
1199
1199
  /**
1200
- * The original request object
1200
+ * The original request object.
1201
1201
  */
1202
1202
  request: Request;
1203
1203
  /**
1204
- * Info about the current route
1204
+ * Info about the current route.
1205
1205
  */
1206
1206
  route: {
1207
1207
  /**
1208
- * The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]`
1208
+ * The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]`.
1209
1209
  */
1210
1210
  id: RouteId;
1211
1211
  };
@@ -1302,15 +1302,16 @@ export class Server {
1302
1302
  }
1303
1303
 
1304
1304
  export interface ServerInitOptions {
1305
- /** A map of environment variables */
1305
+ /** A map of environment variables. */
1306
1306
  env: Record<string, string>;
1307
- /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work */
1307
+ /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work. */
1308
1308
  read?: (file: string) => ReadableStream;
1309
1309
  }
1310
1310
 
1311
1311
  export interface SSRManifest {
1312
1312
  appDir: string;
1313
1313
  appPath: string;
1314
+ /** Static files from `kit.config.files.assets` and the service worker (if any). */
1314
1315
  assets: Set<string>;
1315
1316
  mimeTypes: Record<string, string>;
1316
1317
 
@@ -1321,7 +1322,7 @@ export interface SSRManifest {
1321
1322
  routes: SSRRoute[];
1322
1323
  prerendered_routes: Set<string>;
1323
1324
  matchers: () => Promise<Record<string, ParamMatcher>>;
1324
- /** A `[file]: size` map of all assets imported by server code */
1325
+ /** A `[file]: size` map of all assets imported by server code. */
1325
1326
  server_assets: Record<string, number>;
1326
1327
  };
1327
1328
  }
@@ -1452,7 +1453,7 @@ export interface HttpError {
1452
1453
  }
1453
1454
 
1454
1455
  /**
1455
- * The object returned by the [`redirect`](https://svelte.dev/docs/kit/@sveltejs-kit#redirect) function
1456
+ * The object returned by the [`redirect`](https://svelte.dev/docs/kit/@sveltejs-kit#redirect) function.
1456
1457
  */
1457
1458
  export interface Redirect {
1458
1459
  /** The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages), in the range 300-308. */
@@ -25,45 +25,52 @@ export function build_server_nodes(out, kit, manifest_data, server_manifest, cli
25
25
  /** @type {Set<string>} */
26
26
  const client_stylesheets = new Set();
27
27
  for (const key in client_manifest) {
28
- const file = client_manifest[key];
29
- if (file.css?.[0]) {
30
- client_stylesheets.add(file.css[0]);
31
- }
28
+ client_manifest[key].css?.forEach((filename) => {
29
+ client_stylesheets.add(filename);
30
+ });
32
31
  }
33
32
 
34
- /** @type {Map<number, string>} */
33
+ /** @type {Map<number, string[]>} */
35
34
  const server_stylesheets = new Map();
35
+ manifest_data.nodes.forEach((node, i) => {
36
+ if (!node.component || !server_manifest[node.component]) return;
36
37
 
37
- const component_stylesheet_map = new Map(Object.values(server_manifest).map((file) => [file.src, file.css?.[0]]));
38
+ const { stylesheets } = find_deps(server_manifest, node.component, false);
38
39
 
39
- manifest_data.nodes.forEach((node, i) => {
40
- const server_stylesheet = component_stylesheet_map.get(node.component);
41
- if (node.component && server_stylesheet) {
42
- server_stylesheets.set(i, server_stylesheet);
40
+ if (stylesheets.length) {
41
+ server_stylesheets.set(i, stylesheets);
43
42
  }
44
43
  });
45
44
 
46
- // ignore dynamically imported stylesheets since we can't inline those
47
- css.filter(asset => client_stylesheets.has(asset.fileName))
48
- .forEach((asset) => {
49
- if (asset.source.length < kit.inlineStyleThreshold) {
50
- // We know that the names for entry points are numbers.
51
- const [index] = basename(asset.fileName).split('.');
52
- // There can also be other CSS files from shared components
53
- // for example, which we need to ignore here.
54
- if (isNaN(+index)) return;
55
-
56
- const server_stylesheet = server_stylesheets.get(+index);
57
- const file = `${out}/server/stylesheets/${index}.js`;
58
-
59
- // we need to inline the server stylesheet instead of the client one
60
- // so that asset paths are correct on document load
61
- const source = fs.readFileSync(`${out}/server/${server_stylesheet}`, 'utf-8');
62
-
63
- fs.writeFileSync(file, `// ${server_stylesheet}\nexport default ${s(source)};`);
64
- stylesheet_lookup.set(asset.fileName, index);
65
- }
45
+ for (const asset of css) {
46
+ // ignore dynamically imported stylesheets since we don't need to inline those
47
+ if (!client_stylesheets.has(asset.fileName) || asset.source.length >= kit.inlineStyleThreshold) {
48
+ continue;
49
+ }
50
+
51
+ // We know that the names for entry points are numbers.
52
+ const [index] = basename(asset.fileName).split('.');
53
+ // There can also be other CSS files from shared components
54
+ // for example, which we need to ignore here.
55
+ if (isNaN(+index)) continue;
56
+
57
+ const file = `${out}/server/stylesheets/${index}.js`;
58
+
59
+ // we need to inline the server stylesheet instead of the client one
60
+ // so that asset paths are correct on document load
61
+ const filenames = server_stylesheets.get(+index);
62
+
63
+ if (!filenames) {
64
+ throw new Error('This should never happen, but if it does, it means we failed to find the server stylesheet for a node.');
65
+ }
66
+
67
+ const sources = filenames.map((filename) => {
68
+ return fs.readFileSync(`${out}/server/${filename}`, 'utf-8');
66
69
  });
70
+ fs.writeFileSync(file, `// ${filenames.join(', ')}\nexport default ${s(sources.join('\n'))};`);
71
+
72
+ stylesheet_lookup.set(asset.fileName, index);
73
+ }
67
74
  }
68
75
 
69
76
  manifest_data.nodes.forEach((node, i) => {
@@ -145,6 +145,7 @@ export async function dev(vite, vite_config, svelte_config) {
145
145
  return `${svelte_config.kit.paths.base}${to_fs(svelte_config.kit.outDir)}/generated/client/nodes/${i}.js`;
146
146
  }
147
147
  }),
148
+ // `css` is not necessary in dev, as the JS file from `nodes` will reference the CSS file
148
149
  routes:
149
150
  svelte_config.kit.router.resolution === 'client'
150
151
  ? undefined
@@ -176,17 +177,18 @@ export async function dev(vite, vite_config, svelte_config) {
176
177
  return async () => {
177
178
  /** @type {import('types').SSRNode} */
178
179
  const result = {};
179
-
180
- /** @type {import('vite').ModuleNode[]} */
181
- const module_nodes = [];
182
-
183
180
  result.index = index;
181
+ result.universal_id = node.universal;
182
+ result.server_id = node.server;
184
183
 
185
- // these are unused in dev, it's easier to include them
184
+ // these are unused in dev, but it's easier to include them
186
185
  result.imports = [];
187
186
  result.stylesheets = [];
188
187
  result.fonts = [];
189
188
 
189
+ /** @type {import('vite').ModuleNode[]} */
190
+ const module_nodes = [];
191
+
190
192
  if (node.component) {
191
193
  result.component = async () => {
192
194
  const { module_node, module } = await resolve(
@@ -201,17 +203,13 @@ export async function dev(vite, vite_config, svelte_config) {
201
203
 
202
204
  if (node.universal) {
203
205
  const { module, module_node } = await resolve(node.universal);
204
-
205
206
  module_nodes.push(module_node);
206
-
207
207
  result.universal = module;
208
- result.universal_id = node.universal;
209
208
  }
210
209
 
211
210
  if (node.server) {
212
211
  const { module } = await resolve(node.server);
213
212
  result.server = module;
214
- result.server_id = node.server;
215
213
  }
216
214
 
217
215
  // in dev we inline all styles to avoid FOUC. this gets populated lazily so that
@@ -866,8 +866,12 @@ Tips:
866
866
  /** @type {import('vite').Manifest} */
867
867
  const client_manifest = JSON.parse(read(`${out}/client/${vite_config.build.manifest}`));
868
868
 
869
- const deps_of = /** @param {string} f */ (f) =>
870
- find_deps(client_manifest, posixify(path.relative('.', f)), false);
869
+ /**
870
+ * @param {string} entry
871
+ * @param {boolean} [add_dynamic_css]
872
+ */
873
+ const deps_of = (entry, add_dynamic_css = false) =>
874
+ find_deps(client_manifest, posixify(path.relative('.', entry)), add_dynamic_css);
871
875
 
872
876
  if (svelte_config.kit.output.bundleStrategy === 'split') {
873
877
  const start = deps_of(`${runtime_directory}/client/entry.js`);
@@ -888,14 +892,20 @@ Tips:
888
892
  // similar to that on the client, with as much information computed upfront so that we
889
893
  // don't need to include any code of the actual routes in the server bundle.
890
894
  if (svelte_config.kit.router.resolution === 'server') {
891
- build_data.client.nodes = manifest_data.nodes.map((node, i) => {
895
+ const nodes = manifest_data.nodes.map((node, i) => {
892
896
  if (node.component || node.universal) {
893
- return resolve_symlinks(
897
+ const entry = `${kit.outDir}/generated/client-optimized/nodes/${i}.js`;
898
+ const deps = deps_of(entry, true);
899
+ const file = resolve_symlinks(
894
900
  client_manifest,
895
901
  `${kit.outDir}/generated/client-optimized/nodes/${i}.js`
896
902
  ).chunk.file;
903
+
904
+ return { file, css: deps.stylesheets };
897
905
  }
898
906
  });
907
+ build_data.client.nodes = nodes.map((node) => node?.file);
908
+ build_data.client.css = nodes.map((node) => node?.css);
899
909
 
900
910
  build_data.client.routes = compact(
901
911
  manifest_data.routes.map((route) => {
@@ -19,7 +19,8 @@ import {
19
19
  origin,
20
20
  scroll_state,
21
21
  notifiable_store,
22
- create_updated_store
22
+ create_updated_store,
23
+ load_css
23
24
  } from './utils.js';
24
25
  import { base } from '__sveltekit/paths';
25
26
  import * as devalue from 'devalue';
@@ -41,6 +42,8 @@ import { writable } from 'svelte/store';
41
42
  import { page, update, navigating } from './state.svelte.js';
42
43
  import { add_data_suffix, add_resolution_suffix } from '../pathname.js';
43
44
 
45
+ export { load_css };
46
+
44
47
  const ICON_REL_ATTRIBUTES = new Set(['icon', 'shortcut icon', 'apple-touch-icon']);
45
48
 
46
49
  let errored = false;
@@ -1195,13 +1198,13 @@ async function load_root_error_page({ status, error, url, route }) {
1195
1198
  /**
1196
1199
  * Resolve the relative rerouted URL for a client-side navigation
1197
1200
  * @param {URL} url
1198
- * @returns {URL | undefined}
1201
+ * @returns {Promise<URL | undefined>}
1199
1202
  */
1200
- function get_rerouted_url(url) {
1201
- // reroute could alter the given URL, so we pass a copy
1203
+ async function get_rerouted_url(url) {
1202
1204
  let rerouted;
1203
1205
  try {
1204
- rerouted = app.hooks.reroute({ url: new URL(url) }) ?? url;
1206
+ // reroute could alter the given URL, so we pass a copy
1207
+ rerouted = (await app.hooks.reroute({ url: new URL(url) })) ?? url;
1205
1208
 
1206
1209
  if (typeof rerouted === 'string') {
1207
1210
  const tmp = new URL(url); // do not mutate the incoming URL
@@ -1243,7 +1246,7 @@ async function get_navigation_intent(url, invalidating) {
1243
1246
  if (is_external_url(url, base, app.hash)) return;
1244
1247
 
1245
1248
  if (__SVELTEKIT_CLIENT_ROUTING__) {
1246
- const rerouted = get_rerouted_url(url);
1249
+ const rerouted = await get_rerouted_url(url);
1247
1250
  if (!rerouted) return;
1248
1251
 
1249
1252
  const path = get_url_path(rerouted);
@@ -1633,25 +1636,29 @@ if (import.meta.hot) {
1633
1636
  });
1634
1637
  }
1635
1638
 
1639
+ /** @typedef {(typeof PRELOAD_PRIORITIES)['hover'] | (typeof PRELOAD_PRIORITIES)['tap']} PreloadDataPriority */
1640
+
1636
1641
  function setup_preload() {
1637
1642
  /** @type {NodeJS.Timeout} */
1638
1643
  let mousemove_timeout;
1639
1644
  /** @type {Element} */
1640
1645
  let current_a;
1646
+ /** @type {PreloadDataPriority} */
1647
+ let current_priority;
1641
1648
 
1642
1649
  container.addEventListener('mousemove', (event) => {
1643
1650
  const target = /** @type {Element} */ (event.target);
1644
1651
 
1645
1652
  clearTimeout(mousemove_timeout);
1646
1653
  mousemove_timeout = setTimeout(() => {
1647
- void preload(target, 2);
1654
+ void preload(target, PRELOAD_PRIORITIES.hover);
1648
1655
  }, 20);
1649
1656
  });
1650
1657
 
1651
1658
  /** @param {Event} event */
1652
1659
  function tap(event) {
1653
1660
  if (event.defaultPrevented) return;
1654
- void preload(/** @type {Element} */ (event.composedPath()[0]), 1);
1661
+ void preload(/** @type {Element} */ (event.composedPath()[0]), PRELOAD_PRIORITIES.tap);
1655
1662
  }
1656
1663
 
1657
1664
  container.addEventListener('mousedown', tap);
@@ -1671,13 +1678,14 @@ function setup_preload() {
1671
1678
 
1672
1679
  /**
1673
1680
  * @param {Element} element
1674
- * @param {number} priority
1681
+ * @param {PreloadDataPriority} priority
1675
1682
  */
1676
1683
  async function preload(element, priority) {
1677
1684
  const a = find_anchor(element, container);
1678
- if (!a || a === current_a) return;
1679
1685
 
1680
- current_a = a;
1686
+ // we don't want to preload data again if the user has already hovered/tapped
1687
+ const interacted = a === current_a && priority >= current_priority;
1688
+ if (!a || interacted) return;
1681
1689
 
1682
1690
  const { url, external, download } = get_link_info(a, base, app.hash);
1683
1691
  if (external || download) return;
@@ -1686,29 +1694,34 @@ function setup_preload() {
1686
1694
 
1687
1695
  // we don't want to preload data for a page we're already on
1688
1696
  const same_url = url && get_page_key(current.url) === get_page_key(url);
1689
-
1690
- if (!options.reload && !same_url) {
1691
- if (priority <= options.preload_data) {
1692
- const intent = await get_navigation_intent(url, false);
1693
- if (intent) {
1694
- if (DEV) {
1695
- void _preload_data(intent).then((result) => {
1696
- if (result.type === 'loaded' && result.state.error) {
1697
- console.warn(
1698
- `Preloading data for ${intent.url.pathname} failed with the following error: ${result.state.error.message}\n` +
1699
- 'If this error is transient, you can ignore it. Otherwise, consider disabling preloading for this route. ' +
1700
- 'This route was preloaded due to a data-sveltekit-preload-data attribute. ' +
1701
- 'See https://svelte.dev/docs/kit/link-options for more info'
1702
- );
1703
- }
1704
- });
1705
- } else {
1706
- void _preload_data(intent);
1697
+ if (options.reload || same_url) return;
1698
+
1699
+ if (priority <= options.preload_data) {
1700
+ current_a = a;
1701
+ // we don't want to preload data again on tap if we've already preloaded it on hover
1702
+ current_priority = PRELOAD_PRIORITIES.tap;
1703
+
1704
+ const intent = await get_navigation_intent(url, false);
1705
+ if (!intent) return;
1706
+
1707
+ if (DEV) {
1708
+ void _preload_data(intent).then((result) => {
1709
+ if (result.type === 'loaded' && result.state.error) {
1710
+ console.warn(
1711
+ `Preloading data for ${intent.url.pathname} failed with the following error: ${result.state.error.message}\n` +
1712
+ 'If this error is transient, you can ignore it. Otherwise, consider disabling preloading for this route. ' +
1713
+ 'This route was preloaded due to a data-sveltekit-preload-data attribute. ' +
1714
+ 'See https://svelte.dev/docs/kit/link-options for more info'
1715
+ );
1707
1716
  }
1708
- }
1709
- } else if (priority <= options.preload_code) {
1710
- void _preload_code(/** @type {URL} */ (url));
1717
+ });
1718
+ } else {
1719
+ void _preload_data(intent);
1711
1720
  }
1721
+ } else if (priority <= options.preload_code) {
1722
+ current_a = a;
1723
+ current_priority = priority;
1724
+ void _preload_code(/** @type {URL} */ (url));
1712
1725
  }
1713
1726
  }
1714
1727
 
@@ -1994,7 +2007,7 @@ export async function preloadCode(pathname) {
1994
2007
  }
1995
2008
 
1996
2009
  if (__SVELTEKIT_CLIENT_ROUTING__) {
1997
- const rerouted = get_rerouted_url(url);
2010
+ const rerouted = await get_rerouted_url(url);
1998
2011
  if (!rerouted || !routes.find((route) => route.exec(get_url_path(rerouted)))) {
1999
2012
  throw new Error(`'${pathname}' did not match any routes`);
2000
2013
  }
@@ -2432,6 +2445,12 @@ function _start_router() {
2432
2445
  if (!hash_navigating) {
2433
2446
  const url = new URL(location.href);
2434
2447
  update_url(url);
2448
+
2449
+ // if the user edits the hash via the browser URL bar, trigger a full-page
2450
+ // reload to align with pathname router behavior
2451
+ if (app.hash) {
2452
+ location.reload();
2453
+ }
2435
2454
  }
2436
2455
  }
2437
2456
  });
@@ -2450,13 +2469,6 @@ function _start_router() {
2450
2469
  '',
2451
2470
  location.href
2452
2471
  );
2453
- } else if (app.hash) {
2454
- // If the user edits the hash via the browser URL bar, it
2455
- // (surprisingly!) mutates `current.url`, allowing us to
2456
- // detect it and trigger a navigation
2457
- if (current.url.hash === location.hash) {
2458
- void navigate({ type: 'goto', url: decode_hash(current.url) });
2459
- }
2460
2472
  }
2461
2473
  });
2462
2474
 
@@ -1,3 +1,3 @@
1
1
  // we expose this as a separate entry point (rather than treating client.js as the entry point)
2
- // so that everything other than `start` can be treeshaken
3
- export { start } from './client.js';
2
+ // so that everything other than `start`/`load_css` can be treeshaken
3
+ export { start, load_css } from './client.js';
@@ -331,3 +331,40 @@ export function is_external_url(url, base, hash_routing) {
331
331
 
332
332
  return false;
333
333
  }
334
+
335
+ /** @type {Record<string, boolean>} */
336
+ const seen = {};
337
+
338
+ /**
339
+ * Used for server-side resolution, to replicate Vite's CSS loading behaviour in production.
340
+ *
341
+ * Closely modelled after https://github.com/vitejs/vite/blob/3dd12f4724130fdf8ba44c6d3252ebdff407fd47/packages/vite/src/node/plugins/importAnalysisBuild.ts#L214
342
+ * (which ideally we could just use directly, but it's not exported)
343
+ * @param {string[]} deps
344
+ */
345
+ export function load_css(deps) {
346
+ if (__SVELTEKIT_CLIENT_ROUTING__) return;
347
+
348
+ const csp_nonce_meta = /** @type {HTMLMetaElement} */ (
349
+ document.querySelector('meta[property=csp-nonce]')
350
+ );
351
+ const csp_nonce = csp_nonce_meta?.nonce || csp_nonce_meta?.getAttribute('nonce');
352
+
353
+ for (const dep of deps) {
354
+ if (dep in seen) continue;
355
+ seen[dep] = true;
356
+
357
+ if (document.querySelector(`link[href="${dep}"][rel="stylesheet"]`)) {
358
+ continue;
359
+ }
360
+
361
+ const link = document.createElement('link');
362
+ link.rel = 'stylesheet';
363
+ link.crossOrigin = '';
364
+ link.href = dep;
365
+ if (csp_nonce) {
366
+ link.setAttribute('nonce', csp_nonce);
367
+ }
368
+ document.head.appendChild(link);
369
+ }
370
+ }
@@ -78,7 +78,7 @@ export async function load_server_data({ event, state, node, parent }) {
78
78
  const { href } = new URL(dep, event.url);
79
79
 
80
80
  if (DEV) {
81
- validate_depends(node.server_id, dep);
81
+ validate_depends(node.server_id || 'missing route ID', dep);
82
82
 
83
83
  if (done && !uses.dependencies.has(href)) {
84
84
  console.warn(
@@ -242,7 +242,7 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
242
242
  dependency = { response, body: null };
243
243
  state.prerendering.dependencies.set(url.pathname, dependency);
244
244
  }
245
- } else {
245
+ } else if (url.protocol === 'https:' || url.protocol === 'http:') {
246
246
  // simulate CORS errors and "no access to body in no-cors mode" server-side for consistency with client-side behaviour
247
247
  const mode = input instanceof Request ? input.mode : (init?.mode ?? 'cors');
248
248
  if (mode === 'no-cors') {
@@ -105,10 +105,39 @@ export function create_server_routing_response(route, params, url, manifest) {
105
105
 
106
106
  if (route) {
107
107
  const csr_route = generate_route_object(route, url, manifest);
108
- const body = `export const route = ${csr_route}; export const params = ${JSON.stringify(params)};`;
108
+ const body = `${create_css_import(route, url, manifest)}\nexport const route = ${csr_route}; export const params = ${JSON.stringify(params)};`;
109
109
 
110
110
  return { response: text(body, { headers }), body };
111
111
  } else {
112
112
  return { response: text('', { headers }), body: '' };
113
113
  }
114
114
  }
115
+
116
+ /**
117
+ * This function generates the client-side import for the CSS files that are
118
+ * associated with the current route. Vite takes care of that when using
119
+ * client-side route resolution, but for server-side resolution it does
120
+ * not know about the CSS files automatically.
121
+ *
122
+ * @param {import('types').SSRClientRoute} route
123
+ * @param {URL} url
124
+ * @param {import('@sveltejs/kit').SSRManifest} manifest
125
+ * @returns {string}
126
+ */
127
+ function create_css_import(route, url, manifest) {
128
+ const { errors, layouts, leaf } = route;
129
+
130
+ let css = '';
131
+
132
+ for (const node of [...errors, ...layouts.map((l) => l?.[1]), leaf[1]]) {
133
+ if (typeof node !== 'number') continue;
134
+ const node_css = manifest._.client.css?.[node];
135
+ for (const css_path of node_css ?? []) {
136
+ css += `'${assets || base}/${css_path}',`;
137
+ }
138
+ }
139
+
140
+ if (!css) return '';
141
+
142
+ return `${create_client_import(/** @type {string} */ (manifest._.client.start), url)}.then(x => x.load_css([${css}]));`;
143
+ }
@@ -115,7 +115,7 @@ export async function respond(request, options, manifest, state) {
115
115
 
116
116
  try {
117
117
  // reroute could alter the given URL, so we pass a copy
118
- resolved_path = options.hooks.reroute({ url: new URL(url) }) ?? url.pathname;
118
+ resolved_path = (await options.hooks.reroute({ url: new URL(url) })) ?? url.pathname;
119
119
  } catch {
120
120
  return text('Internal Server Error', {
121
121
  status: 500
@@ -68,18 +68,24 @@ export interface BuildData {
68
68
  out_dir: string;
69
69
  service_worker: string | null;
70
70
  client: {
71
- /** Path to the client entry point */
71
+ /** Path to the client entry point. */
72
72
  start: string;
73
- /** Path to the generated `app.js` file that contains the client manifest. Only set in case of `bundleStrategy === 'split'` */
73
+ /** Path to the generated `app.js` file that contains the client manifest. Only set in case of `bundleStrategy === 'split'`. */
74
74
  app?: string;
75
- /** JS files that the client entry point relies on */
75
+ /** JS files that the client entry point relies on. */
76
76
  imports: string[];
77
77
  /**
78
78
  * JS files that represent the entry points of the layouts/pages.
79
79
  * An entry is undefined if the layout/page has no component or universal file (i.e. only has a `.server.js` file).
80
80
  * Only set in case of `router.resolution === 'server'`.
81
81
  */
82
- nodes?: (string | undefined)[];
82
+ nodes?: Array<string | undefined>;
83
+ /**
84
+ * CSS files referenced in the entry points of the layouts/pages.
85
+ * An entry is undefined if the layout/page has no component or universal file (i.e. only has a `.server.js` file) or if has no CSS.
86
+ * Only set in case of `router.resolution === 'server'`.
87
+ */
88
+ css?: Array<string[] | undefined>;
83
89
  /**
84
90
  * Contains the client route manifest in a form suitable for the server which is used for server side route resolution.
85
91
  * Notably, it contains all routes, regardless of whether they are prerendered or not (those are missing in the optimized server route manifest).
@@ -89,7 +95,7 @@ export interface BuildData {
89
95
  stylesheets: string[];
90
96
  fonts: string[];
91
97
  uses_env_dynamic_public: boolean;
92
- /** Only set in case of `bundleStrategy === 'inline'` */
98
+ /** Only set in case of `bundleStrategy === 'inline'`. */
93
99
  inline?: {
94
100
  script: string;
95
101
  style: string | undefined;
@@ -166,7 +172,7 @@ export class InternalServer extends Server {
166
172
  options: RequestOptions & {
167
173
  prerendering?: PrerenderOptions;
168
174
  read: (file: string) => Buffer;
169
- /** A hook called before `handle` during dev, so that `AsyncLocalStorage` can be populated */
175
+ /** A hook called before `handle` during dev, so that `AsyncLocalStorage` can be populated. */
170
176
  before_handle?: (event: RequestEvent, config: any, prerender: PrerenderOption) => void;
171
177
  emulator?: Emulator;
172
178
  }
@@ -174,6 +180,7 @@ export class InternalServer extends Server {
174
180
  }
175
181
 
176
182
  export interface ManifestData {
183
+ /** Static files from `kit.config.files.assets`. */
177
184
  assets: Asset[];
178
185
  hooks: {
179
186
  client: string | null;
@@ -187,15 +194,15 @@ export interface ManifestData {
187
194
 
188
195
  export interface PageNode {
189
196
  depth: number;
190
- /** The +page/layout.svelte */
197
+ /** The `+page/layout.svelte`. */
191
198
  component?: string; // TODO supply default component if it's missing (bit of an edge case)
192
- /** The +page/layout.js/.ts */
199
+ /** The `+page/layout.js/.ts`. */
193
200
  universal?: string;
194
- /** The +page/layout.server.js/ts */
201
+ /** The `+page/layout.server.js/ts`. */
195
202
  server?: string;
196
203
  parent_id?: string;
197
204
  parent?: PageNode;
198
- /** Filled with the pages that reference this layout (if this is a layout) */
205
+ /** Filled with the pages that reference this layout (if this is a layout). */
199
206
  child_pages?: PageNode[];
200
207
  }
201
208
 
@@ -213,6 +220,7 @@ export interface PrerenderOptions {
213
220
  export type RecursiveRequired<T> = {
214
221
  // Recursive implementation of TypeScript's Required utility type.
215
222
  // Will recursively continue until it reaches a primitive or Function
223
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
216
224
  [K in keyof T]-?: Extract<T[K], Function> extends never // If it does not have a Function type
217
225
  ? RecursiveRequired<T[K]> // recursively continue through.
218
226
  : T[K]; // Use the exact type for everything else
@@ -299,20 +307,20 @@ export interface ServerDataChunkNode {
299
307
 
300
308
  /**
301
309
  * Signals that the server `load` function was not run, and the
302
- * client should use what it has in memory
310
+ * client should use what it has in memory.
303
311
  */
304
312
  export interface ServerDataSkippedNode {
305
313
  type: 'skip';
306
314
  }
307
315
 
308
316
  /**
309
- * Signals that the server `load` function failed
317
+ * Signals that the server `load` function failed.
310
318
  */
311
319
  export interface ServerErrorNode {
312
320
  type: 'error';
313
321
  error: App.Error;
314
322
  /**
315
- * Only set for HttpErrors
323
+ * Only set for HttpErrors.
316
324
  */
317
325
  status?: number;
318
326
  }
@@ -332,7 +340,7 @@ export interface ServerMetadataRoute {
332
340
 
333
341
  export interface ServerMetadata {
334
342
  nodes: Array<{
335
- /** Also `true` when using `trailingSlash`, because we need to do a server request in that case to get its value */
343
+ /** Also `true` when using `trailingSlash`, because we need to do a server request in that case to get its value. */
336
344
  has_server_load: boolean;
337
345
  }>;
338
346
  routes: Map<string, ServerMetadataRoute>;
@@ -358,7 +366,7 @@ export type SSRComponentLoader = () => Promise<SSRComponent>;
358
366
 
359
367
  export interface SSRNode {
360
368
  component: SSRComponentLoader;
361
- /** index into the `nodes` array in the generated `client/app.js` */
369
+ /** index into the `nodes` array in the generated `client/app.js`. */
362
370
  index: number;
363
371
  /** external JS files that are loaded on the client. `imports[0]` is the entry point (e.g. `client/nodes/0.js`) */
364
372
  imports: string[];
@@ -366,7 +374,7 @@ export interface SSRNode {
366
374
  stylesheets: string[];
367
375
  /** external font files that are loaded on the client */
368
376
  fonts: string[];
369
- /** inlined styles */
377
+ /** inlined styles. */
370
378
  inline_styles?(): MaybePromise<Record<string, string>>;
371
379
 
372
380
  universal: {
@@ -390,8 +398,8 @@ export interface SSRNode {
390
398
  entries?: PrerenderEntryGenerator;
391
399
  };
392
400
 
393
- universal_id: string;
394
- server_id: string;
401
+ universal_id?: string;
402
+ server_id?: string;
395
403
  }
396
404
 
397
405
  export type SSRNodeLoader = () => Promise<SSRNode>;
@@ -459,18 +467,18 @@ export interface SSRState {
459
467
  fallback?: string;
460
468
  getClientAddress(): string;
461
469
  /**
462
- * True if we're currently attempting to render an error page
470
+ * True if we're currently attempting to render an error page.
463
471
  */
464
472
  error: boolean;
465
473
  /**
466
- * Allows us to prevent `event.fetch` from making infinitely looping internal requests
474
+ * Allows us to prevent `event.fetch` from making infinitely looping internal requests.
467
475
  */
468
476
  depth: number;
469
477
  platform?: any;
470
478
  prerendering?: PrerenderOptions;
471
479
  /**
472
480
  * When fetching data from a +server.js endpoint in `load`, the page's
473
- * prerender option is inherited by the endpoint, unless overridden
481
+ * prerender option is inherited by the endpoint, unless overridden.
474
482
  */
475
483
  prerender_default?: PrerenderOption;
476
484
  read?: (file: string) => Buffer;
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.17.2';
4
+ export const VERSION = '2.18.0';
package/types/index.d.ts CHANGED
@@ -796,7 +796,7 @@ declare module '@sveltejs/kit' {
796
796
  * The [`reroute`](https://svelte.dev/docs/kit/hooks#Universal-hooks-reroute) hook allows you to modify the URL before it is used to determine which route to render.
797
797
  * @since 2.3.0
798
798
  */
799
- export type Reroute = (event: { url: URL }) => void | string;
799
+ export type Reroute = (event: { url: URL }) => MaybePromise<void | string>;
800
800
 
801
801
  /**
802
802
  * The [`transport`](https://svelte.dev/docs/kit/hooks#Universal-hooks-transport) hook allows you to transport custom types across the server/client boundary.
@@ -1159,7 +1159,7 @@ declare module '@sveltejs/kit' {
1159
1159
  * - During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://svelte.dev/docs/kit/hooks#Server-hooks-handle)
1160
1160
  * - During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.
1161
1161
  *
1162
- * You can learn more about making credentialed requests with cookies [here](https://svelte.dev/docs/kit/load#Cookies)
1162
+ * You can learn more about making credentialed requests with cookies [here](https://svelte.dev/docs/kit/load#Cookies).
1163
1163
  */
1164
1164
  fetch: typeof fetch;
1165
1165
  /**
@@ -1171,7 +1171,7 @@ declare module '@sveltejs/kit' {
1171
1171
  */
1172
1172
  locals: App.Locals;
1173
1173
  /**
1174
- * The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object
1174
+ * The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object.
1175
1175
  */
1176
1176
  params: Params;
1177
1177
  /**
@@ -1179,15 +1179,15 @@ declare module '@sveltejs/kit' {
1179
1179
  */
1180
1180
  platform: Readonly<App.Platform> | undefined;
1181
1181
  /**
1182
- * The original request object
1182
+ * The original request object.
1183
1183
  */
1184
1184
  request: Request;
1185
1185
  /**
1186
- * Info about the current route
1186
+ * Info about the current route.
1187
1187
  */
1188
1188
  route: {
1189
1189
  /**
1190
- * The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]`
1190
+ * The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]`.
1191
1191
  */
1192
1192
  id: RouteId;
1193
1193
  };
@@ -1284,15 +1284,16 @@ declare module '@sveltejs/kit' {
1284
1284
  }
1285
1285
 
1286
1286
  export interface ServerInitOptions {
1287
- /** A map of environment variables */
1287
+ /** A map of environment variables. */
1288
1288
  env: Record<string, string>;
1289
- /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work */
1289
+ /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work. */
1290
1290
  read?: (file: string) => ReadableStream;
1291
1291
  }
1292
1292
 
1293
1293
  export interface SSRManifest {
1294
1294
  appDir: string;
1295
1295
  appPath: string;
1296
+ /** Static files from `kit.config.files.assets` and the service worker (if any). */
1296
1297
  assets: Set<string>;
1297
1298
  mimeTypes: Record<string, string>;
1298
1299
 
@@ -1303,7 +1304,7 @@ declare module '@sveltejs/kit' {
1303
1304
  routes: SSRRoute[];
1304
1305
  prerendered_routes: Set<string>;
1305
1306
  matchers: () => Promise<Record<string, ParamMatcher>>;
1306
- /** A `[file]: size` map of all assets imported by server code */
1307
+ /** A `[file]: size` map of all assets imported by server code. */
1307
1308
  server_assets: Record<string, number>;
1308
1309
  };
1309
1310
  }
@@ -1434,7 +1435,7 @@ declare module '@sveltejs/kit' {
1434
1435
  }
1435
1436
 
1436
1437
  /**
1437
- * The object returned by the [`redirect`](https://svelte.dev/docs/kit/@sveltejs-kit#redirect) function
1438
+ * The object returned by the [`redirect`](https://svelte.dev/docs/kit/@sveltejs-kit#redirect) function.
1438
1439
  */
1439
1440
  export interface Redirect {
1440
1441
  /** The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages), in the range 300-308. */
@@ -1715,18 +1716,24 @@ declare module '@sveltejs/kit' {
1715
1716
  out_dir: string;
1716
1717
  service_worker: string | null;
1717
1718
  client: {
1718
- /** Path to the client entry point */
1719
+ /** Path to the client entry point. */
1719
1720
  start: string;
1720
- /** Path to the generated `app.js` file that contains the client manifest. Only set in case of `bundleStrategy === 'split'` */
1721
+ /** Path to the generated `app.js` file that contains the client manifest. Only set in case of `bundleStrategy === 'split'`. */
1721
1722
  app?: string;
1722
- /** JS files that the client entry point relies on */
1723
+ /** JS files that the client entry point relies on. */
1723
1724
  imports: string[];
1724
1725
  /**
1725
1726
  * JS files that represent the entry points of the layouts/pages.
1726
1727
  * An entry is undefined if the layout/page has no component or universal file (i.e. only has a `.server.js` file).
1727
1728
  * Only set in case of `router.resolution === 'server'`.
1728
1729
  */
1729
- nodes?: (string | undefined)[];
1730
+ nodes?: Array<string | undefined>;
1731
+ /**
1732
+ * CSS files referenced in the entry points of the layouts/pages.
1733
+ * An entry is undefined if the layout/page has no component or universal file (i.e. only has a `.server.js` file) or if has no CSS.
1734
+ * Only set in case of `router.resolution === 'server'`.
1735
+ */
1736
+ css?: Array<string[] | undefined>;
1730
1737
  /**
1731
1738
  * Contains the client route manifest in a form suitable for the server which is used for server side route resolution.
1732
1739
  * Notably, it contains all routes, regardless of whether they are prerendered or not (those are missing in the optimized server route manifest).
@@ -1736,7 +1743,7 @@ declare module '@sveltejs/kit' {
1736
1743
  stylesheets: string[];
1737
1744
  fonts: string[];
1738
1745
  uses_env_dynamic_public: boolean;
1739
- /** Only set in case of `bundleStrategy === 'inline'` */
1746
+ /** Only set in case of `bundleStrategy === 'inline'`. */
1740
1747
  inline?: {
1741
1748
  script: string;
1742
1749
  style: string | undefined;
@@ -1746,6 +1753,7 @@ declare module '@sveltejs/kit' {
1746
1753
  }
1747
1754
 
1748
1755
  interface ManifestData {
1756
+ /** Static files from `kit.config.files.assets`. */
1749
1757
  assets: Asset[];
1750
1758
  hooks: {
1751
1759
  client: string | null;
@@ -1759,21 +1767,22 @@ declare module '@sveltejs/kit' {
1759
1767
 
1760
1768
  interface PageNode {
1761
1769
  depth: number;
1762
- /** The +page/layout.svelte */
1770
+ /** The `+page/layout.svelte`. */
1763
1771
  component?: string; // TODO supply default component if it's missing (bit of an edge case)
1764
- /** The +page/layout.js/.ts */
1772
+ /** The `+page/layout.js/.ts`. */
1765
1773
  universal?: string;
1766
- /** The +page/layout.server.js/ts */
1774
+ /** The `+page/layout.server.js/ts`. */
1767
1775
  server?: string;
1768
1776
  parent_id?: string;
1769
1777
  parent?: PageNode;
1770
- /** Filled with the pages that reference this layout (if this is a layout) */
1778
+ /** Filled with the pages that reference this layout (if this is a layout). */
1771
1779
  child_pages?: PageNode[];
1772
1780
  }
1773
1781
 
1774
1782
  type RecursiveRequired<T> = {
1775
1783
  // Recursive implementation of TypeScript's Required utility type.
1776
1784
  // Will recursively continue until it reaches a primitive or Function
1785
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
1777
1786
  [K in keyof T]-?: Extract<T[K], Function> extends never // If it does not have a Function type
1778
1787
  ? RecursiveRequired<T[K]> // recursively continue through.
1779
1788
  : T[K]; // Use the exact type for everything else
@@ -1835,7 +1844,7 @@ declare module '@sveltejs/kit' {
1835
1844
 
1836
1845
  interface SSRNode {
1837
1846
  component: SSRComponentLoader;
1838
- /** index into the `nodes` array in the generated `client/app.js` */
1847
+ /** index into the `nodes` array in the generated `client/app.js`. */
1839
1848
  index: number;
1840
1849
  /** external JS files that are loaded on the client. `imports[0]` is the entry point (e.g. `client/nodes/0.js`) */
1841
1850
  imports: string[];
@@ -1843,7 +1852,7 @@ declare module '@sveltejs/kit' {
1843
1852
  stylesheets: string[];
1844
1853
  /** external font files that are loaded on the client */
1845
1854
  fonts: string[];
1846
- /** inlined styles */
1855
+ /** inlined styles. */
1847
1856
  inline_styles?(): MaybePromise<Record<string, string>>;
1848
1857
 
1849
1858
  universal: {
@@ -1867,8 +1876,8 @@ declare module '@sveltejs/kit' {
1867
1876
  entries?: PrerenderEntryGenerator;
1868
1877
  };
1869
1878
 
1870
- universal_id: string;
1871
- server_id: string;
1879
+ universal_id?: string;
1880
+ server_id?: string;
1872
1881
  }
1873
1882
 
1874
1883
  type SSRNodeLoader = () => Promise<SSRNode>;
@@ -1996,6 +2005,23 @@ declare module '@sveltejs/kit' {
1996
2005
  * @param e The object to check.
1997
2006
  * */
1998
2007
  export function isActionFailure(e: unknown): e is ActionFailure;
2008
+ /**
2009
+ * Strips possible SvelteKit-internal suffixes and trailing slashes from the URL pathname.
2010
+ * Returns the normalized URL as well as a method for adding the potential suffix back
2011
+ * based on a new pathname (possibly including search) or URL.
2012
+ * ```js
2013
+ * import { normalizeUrl } from '@sveltejs/kit';
2014
+ *
2015
+ * const { url, denormalize } = normalizeUrl('/blog/post/__data.json');
2016
+ * console.log(url.pathname); // /blog/post
2017
+ * console.log(denormalize('/blog/post/a')); // /blog/post/a/__data.json
2018
+ * ```
2019
+ * */
2020
+ export function normalizeUrl(url: URL | string): {
2021
+ url: URL;
2022
+ wasNormalized: boolean;
2023
+ denormalize: (url?: string | URL) => URL;
2024
+ };
1999
2025
  export type LessThan<TNumber extends number, TArray extends any[] = []> = TNumber extends TArray["length"] ? TArray[number] : LessThan<TNumber, [...TArray, TArray["length"]]>;
2000
2026
  export type NumericRange<TStart extends number, TEnd extends number> = Exclude<TEnd | LessThan<TEnd>, LessThan<TStart>>;
2001
2027
  export const VERSION: string;
@@ -88,6 +88,7 @@
88
88
  "json",
89
89
  "text",
90
90
  "isActionFailure",
91
+ "normalizeUrl",
91
92
  "VERSION",
92
93
  "sequence",
93
94
  "getRequest",
@@ -160,6 +161,6 @@
160
161
  null,
161
162
  null
162
163
  ],
163
- "mappings": "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",
164
+ "mappings": "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",
164
165
  "ignoreList": []
165
166
  }