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

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,32 @@ 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
+ // TODO remove this for 1.0
832
+ /**
833
+ * @param {string} property
834
+ * @param {string} replacement
835
+ */
836
+ const print_error = (property, replacement) => {
837
+ Object.defineProperty(result.props.page, property, {
838
+ get: () => {
839
+ throw new Error(`$page.${property} has been replaced by $page.url.${replacement}`);
840
+ }
841
+ });
842
+ };
843
+
844
+ print_error('origin', 'origin');
845
+ print_error('path', 'pathname');
846
+ print_error('query', 'searchParams');
848
847
  }
849
848
 
850
849
  const leaf = filtered[filtered.length - 1];
851
850
  const maxage = leaf.loaded && leaf.loaded.maxage;
852
851
 
853
852
  if (maxage) {
854
- const key = `${page.path}?${page.query}`;
853
+ const key = url.pathname + url.search; // omit hash
855
854
  let ready = false;
856
855
 
857
856
  const clear = () => {
@@ -882,19 +881,19 @@ class Renderer {
882
881
  * status?: number;
883
882
  * error?: Error;
884
883
  * module: CSRComponent;
885
- * page: import('types/page').Page;
884
+ * url: URL;
885
+ * params: Record<string, string>;
886
886
  * stuff: Record<string, any>;
887
887
  * }} options
888
888
  * @returns
889
889
  */
890
- async _load_node({ status, error, module, page, stuff }) {
890
+ async _load_node({ status, error, module, url, params, stuff }) {
891
891
  /** @type {import('./types').BranchNode} */
892
892
  const node = {
893
893
  module,
894
894
  uses: {
895
895
  params: new Set(),
896
- path: false,
897
- query: false,
896
+ url: false,
898
897
  session: false,
899
898
  stuff: false,
900
899
  dependencies: []
@@ -904,12 +903,12 @@ class Renderer {
904
903
  };
905
904
 
906
905
  /** @type {Record<string, string>} */
907
- const params = {};
908
- for (const key in page.params) {
909
- Object.defineProperty(params, key, {
906
+ const uses_params = {};
907
+ for (const key in params) {
908
+ Object.defineProperty(uses_params, key, {
910
909
  get() {
911
910
  node.uses.params.add(key);
912
- return page.params[key];
911
+ return params[key];
913
912
  },
914
913
  enumerable: true
915
914
  });
@@ -922,17 +921,10 @@ class Renderer {
922
921
 
923
922
  /** @type {import('types/page').LoadInput | import('types/page').ErrorLoadInput} */
924
923
  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
- }
924
+ params: uses_params,
925
+ get url() {
926
+ node.uses.url = true;
927
+ return url;
936
928
  },
937
929
  get session() {
938
930
  node.uses.session = true;
@@ -943,14 +935,23 @@ class Renderer {
943
935
  return { ...stuff };
944
936
  },
945
937
  fetch(resource, info) {
946
- const url = typeof resource === 'string' ? resource : resource.url;
947
- const { href } = new URL(url, new URL(page.path, document.baseURI));
938
+ const requested = typeof resource === 'string' ? resource : resource.url;
939
+ const { href } = new URL(requested, url);
948
940
  node.uses.dependencies.push(href);
949
941
 
950
942
  return started ? fetch(resource, info) : initial_fetch(resource, info);
951
943
  }
952
944
  };
953
945
 
946
+ if (import.meta.env.DEV) {
947
+ // TODO remove this for 1.0
948
+ Object.defineProperty(load_input, 'page', {
949
+ get: () => {
950
+ throw new Error('`page` in `load` functions has been replaced by `url` and `params`');
951
+ }
952
+ });
953
+ }
954
+
954
955
  if (error) {
955
956
  /** @type {import('types/page').ErrorLoadInput} */ (load_input).status = status;
956
957
  /** @type {import('types/page').ErrorLoadInput} */ (load_input).error = error;
@@ -973,8 +974,8 @@ class Renderer {
973
974
  * @param {boolean} no_cache
974
975
  * @returns {Promise<import('./types').NavigationResult | undefined>} undefined if fallthrough
975
976
  */
976
- async _load({ route, info: { path, decoded_path, query } }, no_cache) {
977
- const key = `${decoded_path}?${query}`;
977
+ async _load({ route, info: { url, path } }, no_cache) {
978
+ const key = url.pathname + url.search;
978
979
 
979
980
  if (!no_cache) {
980
981
  const cached = this.cache.get(key);
@@ -984,19 +985,15 @@ class Renderer {
984
985
  const [pattern, a, b, get_params] = route;
985
986
  const params = get_params
986
987
  ? // the pattern is for the route which we've already matched to this path
987
- get_params(/** @type {RegExpExecArray} */ (pattern.exec(decoded_path)))
988
+ get_params(/** @type {RegExpExecArray} */ (pattern.exec(path)))
988
989
  : {};
989
990
 
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(),
991
+ const changed = this.current.url && {
992
+ url: key !== this.current.url.pathname + this.current.url.search,
993
+ params: Object.keys(params).filter((key) => this.current.params[key] !== params[key]),
994
994
  session: this.session_id !== this.current.session_id
995
995
  };
996
996
 
997
- /** @type {import('types/page').Page} */
998
- const page = { host: this.host, path, query, params };
999
-
1000
997
  /** @type {Array<import('./types').BranchNode | undefined>} */
1001
998
  let branch = [];
1002
999
 
@@ -1026,9 +1023,8 @@ class Renderer {
1026
1023
  const changed_since_last_render =
1027
1024
  !previous ||
1028
1025
  module !== previous.module ||
1029
- (changed.path && previous.uses.path) ||
1026
+ (changed.url && previous.uses.url) ||
1030
1027
  changed.params.some((param) => previous.uses.params.has(param)) ||
1031
- (changed.query && previous.uses.query) ||
1032
1028
  (changed.session && previous.uses.session) ||
1033
1029
  previous.uses.dependencies.some((dep) => this.invalid.has(dep)) ||
1034
1030
  (stuff_changed && previous.uses.stuff);
@@ -1036,7 +1032,8 @@ class Renderer {
1036
1032
  if (changed_since_last_render) {
1037
1033
  node = await this._load_node({
1038
1034
  module,
1039
- page,
1035
+ url,
1036
+ params,
1040
1037
  stuff
1041
1038
  });
1042
1039
 
@@ -1089,7 +1086,8 @@ class Renderer {
1089
1086
  status,
1090
1087
  error,
1091
1088
  module: await b[i](),
1092
- page,
1089
+ url,
1090
+ params,
1093
1091
  stuff: node_loaded.stuff
1094
1092
  });
1095
1093
 
@@ -1108,8 +1106,7 @@ class Renderer {
1108
1106
  return await this._load_error({
1109
1107
  status,
1110
1108
  error,
1111
- path,
1112
- query
1109
+ url
1113
1110
  });
1114
1111
  } else {
1115
1112
  if (node && node.loaded && node.loaded.stuff) {
@@ -1123,28 +1120,24 @@ class Renderer {
1123
1120
  }
1124
1121
  }
1125
1122
 
1126
- return await this._get_navigation_result_from_branch({ page, branch });
1123
+ return await this._get_navigation_result_from_branch({ url, params, branch });
1127
1124
  }
1128
1125
 
1129
1126
  /**
1130
1127
  * @param {{
1131
1128
  * status?: number;
1132
1129
  * error: Error;
1133
- * path: string;
1134
- * query: URLSearchParams
1130
+ * url: URL;
1135
1131
  * }} opts
1136
1132
  */
1137
- async _load_error({ status, error, path, query }) {
1138
- const page = {
1139
- host: this.host,
1140
- path,
1141
- query,
1142
- params: {}
1143
- };
1133
+ async _load_error({ status, error, url }) {
1134
+ /** @type {Record<string, string>} */
1135
+ const params = {}; // error page does not have params
1144
1136
 
1145
1137
  const node = await this._load_node({
1146
1138
  module: await this.fallback[0],
1147
- page,
1139
+ url,
1140
+ params,
1148
1141
  stuff: {}
1149
1142
  });
1150
1143
 
@@ -1154,12 +1147,13 @@ class Renderer {
1154
1147
  status,
1155
1148
  error,
1156
1149
  module: await this.fallback[1],
1157
- page,
1150
+ url,
1151
+ params,
1158
1152
  stuff: (node && node.loaded && node.loaded.stuff) || {}
1159
1153
  })
1160
1154
  ];
1161
1155
 
1162
- return await this._get_navigation_result_from_branch({ page, branch });
1156
+ return await this._get_navigation_result_from_branch({ url, params, branch });
1163
1157
  }
1164
1158
  }
1165
1159
 
@@ -1173,7 +1167,6 @@ class Renderer {
1173
1167
  * },
1174
1168
  * target: Node;
1175
1169
  * session: any;
1176
- * host: string;
1177
1170
  * route: boolean;
1178
1171
  * spa: boolean;
1179
1172
  * trailing_slash: import('types/internal').TrailingSlash;
@@ -1181,11 +1174,12 @@ class Renderer {
1181
1174
  * status: number;
1182
1175
  * error: Error;
1183
1176
  * nodes: Array<Promise<import('types/internal').CSRComponent>>;
1184
- * page: import('types/page').Page;
1177
+ * url: URL;
1178
+ * params: Record<string, string>;
1185
1179
  * };
1186
1180
  * }} opts
1187
1181
  */
1188
- async function start({ paths, target, session, host, route, spa, trailing_slash, hydrate }) {
1182
+ async function start({ paths, target, session, route, spa, trailing_slash, hydrate }) {
1189
1183
  if (import.meta.env.DEV && !target) {
1190
1184
  throw new Error('Missing target element. See https://kit.svelte.dev/docs#configuration-target');
1191
1185
  }
@@ -1194,8 +1188,7 @@ async function start({ paths, target, session, host, route, spa, trailing_slash,
1194
1188
  Root,
1195
1189
  fallback,
1196
1190
  target,
1197
- session,
1198
- host
1191
+ session
1199
1192
  });
1200
1193
 
1201
1194
  const router = route