@sveltejs/kit 1.0.0-next.206 → 1.0.0-next.208

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.
@@ -195,15 +195,14 @@ class Router {
195
195
  */
196
196
  parse(url) {
197
197
  if (this.owns(url)) {
198
- const path = url.pathname.slice(this.base.length) || '/';
198
+ const path = decodeURI(url.pathname.slice(this.base.length) || '/');
199
199
 
200
- const decoded_path = decodeURI(path);
201
- const routes = this.routes.filter(([pattern]) => pattern.test(decoded_path));
202
-
203
- const query = new URLSearchParams(url.search);
204
- const id = `${path}?${query}`;
205
-
206
- return { id, routes, path, decoded_path, query };
200
+ return {
201
+ id: url.pathname + url.search,
202
+ routes: this.routes.filter(([pattern]) => pattern.test(path)),
203
+ url,
204
+ path
205
+ };
207
206
  }
208
207
  }
209
208
 
@@ -273,22 +272,18 @@ class Router {
273
272
  }
274
273
  this.navigating++;
275
274
 
276
- // remove trailing slashes
277
- if (info.path !== '/') {
278
- const has_trailing_slash = info.path.endsWith('/');
279
-
280
- const incorrect =
281
- (has_trailing_slash && this.trailing_slash === 'never') ||
282
- (!has_trailing_slash &&
283
- this.trailing_slash === 'always' &&
284
- !(info.path.split('/').pop() || '').includes('.'));
275
+ let { pathname } = url;
285
276
 
286
- if (incorrect) {
287
- info.path = has_trailing_slash ? info.path.slice(0, -1) : info.path + '/';
288
- history.replaceState({}, '', `${this.base}${info.path}${location.search}`);
289
- }
277
+ if (this.trailing_slash === 'never') {
278
+ if (pathname !== '/' && pathname.endsWith('/')) pathname = pathname.slice(0, -1);
279
+ } else if (this.trailing_slash === 'always') {
280
+ const is_file = /** @type {string} */ (url.pathname.split('/').pop()).includes('.');
281
+ if (!is_file && !pathname.endsWith('/')) pathname += '/';
290
282
  }
291
283
 
284
+ info.url = new URL(url.origin + pathname + url.search + url.hash);
285
+ history.replaceState({}, '', info.url);
286
+
292
287
  await this.renderer.handle_navigation(info, chain, false, { hash, scroll, keepfocus });
293
288
 
294
289
  this.navigating--;
@@ -393,13 +388,11 @@ function normalize(loaded) {
393
388
 
394
389
  /**
395
390
  * @typedef {import('types/internal').CSRComponent} CSRComponent
396
- *
397
- * @typedef {Partial<import('types/page').Page>} Page
398
- * @typedef {{ from: Page; to: Page }} Navigating
391
+ * @typedef {{ from: URL; to: URL }} Navigating
399
392
  */
400
393
 
401
394
  /** @param {any} value */
402
- function page_store(value) {
395
+ function notifiable_store(value) {
403
396
  const store = writable(value);
404
397
  let ready = true;
405
398
 
@@ -457,13 +450,11 @@ class Renderer {
457
450
  * fallback: [CSRComponent, CSRComponent];
458
451
  * target: Node;
459
452
  * session: any;
460
- * host: string;
461
453
  * }} opts
462
454
  */
463
- constructor({ Root, fallback, target, session, host }) {
455
+ constructor({ Root, fallback, target, session }) {
464
456
  this.Root = Root;
465
457
  this.fallback = fallback;
466
- this.host = host;
467
458
 
468
459
  /** @type {import('./router').Router | undefined} */
469
460
  this.router;
@@ -479,7 +470,7 @@ class Renderer {
479
470
  /** @type {import('./types').NavigationState} */
480
471
  this.current = {
481
472
  // @ts-ignore - we need the initial value to be null
482
- page: null,
473
+ url: null,
483
474
  session_id: 0,
484
475
  branch: []
485
476
  };
@@ -494,7 +485,8 @@ class Renderer {
494
485
  };
495
486
 
496
487
  this.stores = {
497
- page: page_store({}),
488
+ url: notifiable_store({}),
489
+ page: notifiable_store({}),
498
490
  navigating: writable(/** @type {Navigating | null} */ (null)),
499
491
  session: writable(session)
500
492
  };
@@ -521,10 +513,11 @@ class Renderer {
521
513
  * status: number;
522
514
  * error: Error;
523
515
  * nodes: Array<Promise<CSRComponent>>;
524
- * page: import('types/page').Page;
516
+ * url: URL;
517
+ * params: Record<string, string>;
525
518
  * }} selected
526
519
  */
527
- async start({ status, error, nodes, page }) {
520
+ async start({ status, error, nodes, url, params }) {
528
521
  /** @type {Array<import('./types').BranchNode | undefined>} */
529
522
  const branch = [];
530
523
 
@@ -542,7 +535,8 @@ class Renderer {
542
535
 
543
536
  const node = await this._load_node({
544
537
  module: await nodes[i],
545
- page,
538
+ url,
539
+ params,
546
540
  stuff,
547
541
  status: is_leaf ? status : undefined,
548
542
  error: is_leaf ? error : undefined
@@ -556,8 +550,7 @@ class Renderer {
556
550
  error_args = {
557
551
  status: node.loaded.status,
558
552
  error: node.loaded.error,
559
- path: page.path,
560
- query: page.query
553
+ url
561
554
  };
562
555
  } else if (node.loaded.stuff) {
563
556
  stuff = {
@@ -570,15 +563,14 @@ class Renderer {
570
563
 
571
564
  result = error_args
572
565
  ? await this._load_error(error_args)
573
- : await this._get_navigation_result_from_branch({ page, branch });
566
+ : await this._get_navigation_result_from_branch({ url, params, branch });
574
567
  } catch (e) {
575
568
  if (error) throw e;
576
569
 
577
570
  result = await this._load_error({
578
571
  status: 500,
579
572
  error: coalesce_to_error(e),
580
- path: page.path,
581
- query: page.query
573
+ url
582
574
  });
583
575
  }
584
576
 
@@ -601,14 +593,8 @@ class Renderer {
601
593
  async handle_navigation(info, chain, no_cache, opts) {
602
594
  if (this.started) {
603
595
  this.stores.navigating.set({
604
- from: {
605
- path: this.current.page.path,
606
- query: this.current.page.query
607
- },
608
- to: {
609
- path: info.path,
610
- query: info.query
611
- }
596
+ from: this.current.url,
597
+ to: info.url
612
598
  });
613
599
  }
614
600
 
@@ -631,18 +617,17 @@ class Renderer {
631
617
  this.invalid.clear();
632
618
 
633
619
  if (navigation_result.redirect) {
634
- if (chain.length > 10 || chain.includes(info.path)) {
620
+ if (chain.length > 10 || chain.includes(info.url.pathname)) {
635
621
  navigation_result = await this._load_error({
636
622
  status: 500,
637
623
  error: new Error('Redirect loop'),
638
- path: info.path,
639
- query: info.query
624
+ url: info.url
640
625
  });
641
626
  } else {
642
627
  if (this.router) {
643
628
  this.router.goto(navigation_result.redirect, { replaceState: true }, [
644
629
  ...chain,
645
- info.path
630
+ info.url.pathname
646
631
  ]);
647
632
  } else {
648
633
  location.href = new URL(navigation_result.redirect, location.href).href;
@@ -804,20 +789,20 @@ class Renderer {
804
789
 
805
790
  return await this._load_error({
806
791
  status: 404,
807
- error: new Error(`Not found: ${info.path}`),
808
- path: info.path,
809
- query: info.query
792
+ error: new Error(`Not found: ${info.url.pathname}`),
793
+ url: info.url
810
794
  });
811
795
  }
812
796
 
813
797
  /**
814
798
  *
815
799
  * @param {{
816
- * page: import('types/page').Page;
817
- * branch: Array<import('./types').BranchNode | undefined>
800
+ * url: URL;
801
+ * params: Record<string, string>;
802
+ * branch: Array<import('./types').BranchNode | undefined>;
818
803
  * }} opts
819
804
  */
820
- async _get_navigation_result_from_branch({ page, branch }) {
805
+ async _get_navigation_result_from_branch({ url, params, branch }) {
821
806
  const filtered = /** @type {import('./types').BranchNode[] } */ (branch.filter(Boolean));
822
807
  const redirect = filtered.find((f) => f.loaded && f.loaded.redirect);
823
808
 
@@ -825,7 +810,8 @@ class Renderer {
825
810
  const result = {
826
811
  redirect: redirect && redirect.loaded ? redirect.loaded.redirect : undefined,
827
812
  state: {
828
- page,
813
+ url,
814
+ params,
829
815
  branch,
830
816
  session_id: this.session_id
831
817
  },
@@ -839,19 +825,34 @@ class Renderer {
839
825
  result.props[`props_${i}`] = loaded ? await loaded.props : null;
840
826
  }
841
827
 
842
- if (
843
- !this.current.page ||
844
- page.path !== this.current.page.path ||
845
- page.query.toString() !== this.current.page.query.toString()
846
- ) {
847
- result.props.page = page;
828
+ if (!this.current.url || url.href !== this.current.url.href) {
829
+ result.props.page = { url, params };
830
+
831
+ if (import.meta.env.DEV) {
832
+ // TODO remove this for 1.0
833
+ /**
834
+ * @param {string} property
835
+ * @param {string} replacement
836
+ */
837
+ const print_error = (property, replacement) => {
838
+ Object.defineProperty(result.props.page, property, {
839
+ get: () => {
840
+ throw new Error(`$page.${property} has been replaced by $page.url.${replacement}`);
841
+ }
842
+ });
843
+ };
844
+
845
+ print_error('origin', 'origin');
846
+ print_error('path', 'pathname');
847
+ print_error('query', 'searchParams');
848
+ }
848
849
  }
849
850
 
850
851
  const leaf = filtered[filtered.length - 1];
851
852
  const maxage = leaf.loaded && leaf.loaded.maxage;
852
853
 
853
854
  if (maxage) {
854
- const key = `${page.path}?${page.query}`;
855
+ const key = url.pathname + url.search; // omit hash
855
856
  let ready = false;
856
857
 
857
858
  const clear = () => {
@@ -882,19 +883,19 @@ class Renderer {
882
883
  * status?: number;
883
884
  * error?: Error;
884
885
  * module: CSRComponent;
885
- * page: import('types/page').Page;
886
+ * url: URL;
887
+ * params: Record<string, string>;
886
888
  * stuff: Record<string, any>;
887
889
  * }} options
888
890
  * @returns
889
891
  */
890
- async _load_node({ status, error, module, page, stuff }) {
892
+ async _load_node({ status, error, module, url, params, stuff }) {
891
893
  /** @type {import('./types').BranchNode} */
892
894
  const node = {
893
895
  module,
894
896
  uses: {
895
897
  params: new Set(),
896
- path: false,
897
- query: false,
898
+ url: false,
898
899
  session: false,
899
900
  stuff: false,
900
901
  dependencies: []
@@ -904,12 +905,12 @@ class Renderer {
904
905
  };
905
906
 
906
907
  /** @type {Record<string, string>} */
907
- const params = {};
908
- for (const key in page.params) {
909
- Object.defineProperty(params, key, {
908
+ const uses_params = {};
909
+ for (const key in params) {
910
+ Object.defineProperty(uses_params, key, {
910
911
  get() {
911
912
  node.uses.params.add(key);
912
- return page.params[key];
913
+ return params[key];
913
914
  },
914
915
  enumerable: true
915
916
  });
@@ -922,17 +923,10 @@ class Renderer {
922
923
 
923
924
  /** @type {import('types/page').LoadInput | import('types/page').ErrorLoadInput} */
924
925
  const load_input = {
925
- page: {
926
- host: page.host,
927
- params,
928
- get path() {
929
- node.uses.path = true;
930
- return page.path;
931
- },
932
- get query() {
933
- node.uses.query = true;
934
- return page.query;
935
- }
926
+ params: uses_params,
927
+ get url() {
928
+ node.uses.url = true;
929
+ return url;
936
930
  },
937
931
  get session() {
938
932
  node.uses.session = true;
@@ -943,14 +937,23 @@ class Renderer {
943
937
  return { ...stuff };
944
938
  },
945
939
  fetch(resource, info) {
946
- const url = typeof resource === 'string' ? resource : resource.url;
947
- const { href } = new URL(url, new URL(page.path, document.baseURI));
940
+ const requested = typeof resource === 'string' ? resource : resource.url;
941
+ const { href } = new URL(requested, url);
948
942
  node.uses.dependencies.push(href);
949
943
 
950
944
  return started ? fetch(resource, info) : initial_fetch(resource, info);
951
945
  }
952
946
  };
953
947
 
948
+ if (import.meta.env.DEV) {
949
+ // TODO remove this for 1.0
950
+ Object.defineProperty(load_input, 'page', {
951
+ get: () => {
952
+ throw new Error('`page` in `load` functions has been replaced by `url` and `params`');
953
+ }
954
+ });
955
+ }
956
+
954
957
  if (error) {
955
958
  /** @type {import('types/page').ErrorLoadInput} */ (load_input).status = status;
956
959
  /** @type {import('types/page').ErrorLoadInput} */ (load_input).error = error;
@@ -973,8 +976,8 @@ class Renderer {
973
976
  * @param {boolean} no_cache
974
977
  * @returns {Promise<import('./types').NavigationResult | undefined>} undefined if fallthrough
975
978
  */
976
- async _load({ route, info: { path, decoded_path, query } }, no_cache) {
977
- const key = `${decoded_path}?${query}`;
979
+ async _load({ route, info: { url, path } }, no_cache) {
980
+ const key = url.pathname + url.search;
978
981
 
979
982
  if (!no_cache) {
980
983
  const cached = this.cache.get(key);
@@ -984,19 +987,15 @@ class Renderer {
984
987
  const [pattern, a, b, get_params] = route;
985
988
  const params = get_params
986
989
  ? // the pattern is for the route which we've already matched to this path
987
- get_params(/** @type {RegExpExecArray} */ (pattern.exec(decoded_path)))
990
+ get_params(/** @type {RegExpExecArray} */ (pattern.exec(path)))
988
991
  : {};
989
992
 
990
- const changed = this.current.page && {
991
- path: path !== this.current.page.path,
992
- params: Object.keys(params).filter((key) => this.current.page.params[key] !== params[key]),
993
- query: query.toString() !== this.current.page.query.toString(),
993
+ const changed = this.current.url && {
994
+ url: key !== this.current.url.pathname + this.current.url.search,
995
+ params: Object.keys(params).filter((key) => this.current.params[key] !== params[key]),
994
996
  session: this.session_id !== this.current.session_id
995
997
  };
996
998
 
997
- /** @type {import('types/page').Page} */
998
- const page = { host: this.host, path, query, params };
999
-
1000
999
  /** @type {Array<import('./types').BranchNode | undefined>} */
1001
1000
  let branch = [];
1002
1001
 
@@ -1026,9 +1025,8 @@ class Renderer {
1026
1025
  const changed_since_last_render =
1027
1026
  !previous ||
1028
1027
  module !== previous.module ||
1029
- (changed.path && previous.uses.path) ||
1028
+ (changed.url && previous.uses.url) ||
1030
1029
  changed.params.some((param) => previous.uses.params.has(param)) ||
1031
- (changed.query && previous.uses.query) ||
1032
1030
  (changed.session && previous.uses.session) ||
1033
1031
  previous.uses.dependencies.some((dep) => this.invalid.has(dep)) ||
1034
1032
  (stuff_changed && previous.uses.stuff);
@@ -1036,7 +1034,8 @@ class Renderer {
1036
1034
  if (changed_since_last_render) {
1037
1035
  node = await this._load_node({
1038
1036
  module,
1039
- page,
1037
+ url,
1038
+ params,
1040
1039
  stuff
1041
1040
  });
1042
1041
 
@@ -1089,7 +1088,8 @@ class Renderer {
1089
1088
  status,
1090
1089
  error,
1091
1090
  module: await b[i](),
1092
- page,
1091
+ url,
1092
+ params,
1093
1093
  stuff: node_loaded.stuff
1094
1094
  });
1095
1095
 
@@ -1108,8 +1108,7 @@ class Renderer {
1108
1108
  return await this._load_error({
1109
1109
  status,
1110
1110
  error,
1111
- path,
1112
- query
1111
+ url
1113
1112
  });
1114
1113
  } else {
1115
1114
  if (node && node.loaded && node.loaded.stuff) {
@@ -1123,28 +1122,24 @@ class Renderer {
1123
1122
  }
1124
1123
  }
1125
1124
 
1126
- return await this._get_navigation_result_from_branch({ page, branch });
1125
+ return await this._get_navigation_result_from_branch({ url, params, branch });
1127
1126
  }
1128
1127
 
1129
1128
  /**
1130
1129
  * @param {{
1131
1130
  * status?: number;
1132
1131
  * error: Error;
1133
- * path: string;
1134
- * query: URLSearchParams
1132
+ * url: URL;
1135
1133
  * }} opts
1136
1134
  */
1137
- async _load_error({ status, error, path, query }) {
1138
- const page = {
1139
- host: this.host,
1140
- path,
1141
- query,
1142
- params: {}
1143
- };
1135
+ async _load_error({ status, error, url }) {
1136
+ /** @type {Record<string, string>} */
1137
+ const params = {}; // error page does not have params
1144
1138
 
1145
1139
  const node = await this._load_node({
1146
1140
  module: await this.fallback[0],
1147
- page,
1141
+ url,
1142
+ params,
1148
1143
  stuff: {}
1149
1144
  });
1150
1145
 
@@ -1154,12 +1149,13 @@ class Renderer {
1154
1149
  status,
1155
1150
  error,
1156
1151
  module: await this.fallback[1],
1157
- page,
1152
+ url,
1153
+ params,
1158
1154
  stuff: (node && node.loaded && node.loaded.stuff) || {}
1159
1155
  })
1160
1156
  ];
1161
1157
 
1162
- return await this._get_navigation_result_from_branch({ page, branch });
1158
+ return await this._get_navigation_result_from_branch({ url, params, branch });
1163
1159
  }
1164
1160
  }
1165
1161
 
@@ -1173,7 +1169,6 @@ class Renderer {
1173
1169
  * },
1174
1170
  * target: Node;
1175
1171
  * session: any;
1176
- * host: string;
1177
1172
  * route: boolean;
1178
1173
  * spa: boolean;
1179
1174
  * trailing_slash: import('types/internal').TrailingSlash;
@@ -1181,11 +1176,12 @@ class Renderer {
1181
1176
  * status: number;
1182
1177
  * error: Error;
1183
1178
  * nodes: Array<Promise<import('types/internal').CSRComponent>>;
1184
- * page: import('types/page').Page;
1179
+ * url: URL;
1180
+ * params: Record<string, string>;
1185
1181
  * };
1186
1182
  * }} opts
1187
1183
  */
1188
- async function start({ paths, target, session, host, route, spa, trailing_slash, hydrate }) {
1184
+ async function start({ paths, target, session, route, spa, trailing_slash, hydrate }) {
1189
1185
  if (import.meta.env.DEV && !target) {
1190
1186
  throw new Error('Missing target element. See https://kit.svelte.dev/docs#configuration-target');
1191
1187
  }
@@ -1194,8 +1190,7 @@ async function start({ paths, target, session, host, route, spa, trailing_slash,
1194
1190
  Root,
1195
1191
  fallback,
1196
1192
  target,
1197
- session,
1198
- host
1193
+ session
1199
1194
  });
1200
1195
 
1201
1196
  const router = route