@sveltejs/kit 1.0.0-next.219 → 1.0.0-next.222

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/assets/kit.js CHANGED
@@ -112,13 +112,14 @@ async function render_endpoint(request, route, match) {
112
112
  const response = await handler(request);
113
113
  const preface = `Invalid response from route ${request.url.pathname}`;
114
114
 
115
- if (!response) {
116
- return;
117
- }
118
115
  if (typeof response !== 'object') {
119
116
  return error(`${preface}: expected an object, got ${typeof response}`);
120
117
  }
121
118
 
119
+ if (response.fallthrough) {
120
+ return;
121
+ }
122
+
122
123
  let { status = 200, body, headers = {} } = response;
123
124
 
124
125
  headers = lowercase_keys(headers);
@@ -543,11 +544,13 @@ const s = JSON.stringify;
543
544
  * branch: Array<import('./types').Loaded>;
544
545
  * options: import('types/internal').SSRRenderOptions;
545
546
  * $session: any;
546
- * page_config: { hydrate: boolean, router: boolean, ssr: boolean };
547
+ * page_config: { hydrate: boolean, router: boolean };
547
548
  * status: number;
548
- * error?: Error,
549
+ * error?: Error;
549
550
  * url: URL;
550
- * params: Record<string, string>
551
+ * params: Record<string, string>;
552
+ * ssr: boolean;
553
+ * stuff: Record<string, any>;
551
554
  * }} opts
552
555
  */
553
556
  async function render_response({
@@ -558,7 +561,9 @@ async function render_response({
558
561
  status,
559
562
  error,
560
563
  url,
561
- params
564
+ params,
565
+ ssr,
566
+ stuff
562
567
  }) {
563
568
  const css = new Set(options.manifest._.entry.css);
564
569
  const js = new Set(options.manifest._.entry.js);
@@ -576,7 +581,7 @@ async function render_response({
576
581
  error.stack = options.get_stack(error);
577
582
  }
578
583
 
579
- if (page_config.ssr) {
584
+ if (ssr) {
580
585
  branch.forEach(({ node, loaded, fetched, uses_credentials }) => {
581
586
  if (node.css) node.css.forEach((url) => css.add(url));
582
587
  if (node.js) node.js.forEach((url) => js.add(url));
@@ -599,7 +604,7 @@ async function render_response({
599
604
  navigating: writable(null),
600
605
  session
601
606
  },
602
- page: { url, params, status, error },
607
+ page: { url, params, status, error, stuff },
603
608
  components: branch.map(({ node }) => node.module.default)
604
609
  };
605
610
 
@@ -679,9 +684,9 @@ async function render_response({
679
684
  throw new Error(`Failed to serialize session data: ${error.message}`);
680
685
  })},
681
686
  route: ${!!page_config.router},
682
- spa: ${!page_config.ssr},
687
+ spa: ${!ssr},
683
688
  trailing_slash: ${s(options.trailing_slash)},
684
- hydrate: ${page_config.ssr && page_config.hydrate ? `{
689
+ hydrate: ${ssr && page_config.hydrate ? `{
685
690
  status: ${status},
686
691
  error: ${serialize_error(error)},
687
692
  nodes: [
@@ -904,7 +909,6 @@ function is_root_relative(path) {
904
909
  * $session: any;
905
910
  * stuff: Record<string, any>;
906
911
  * prerender_enabled: boolean;
907
- * is_leaf: boolean;
908
912
  * is_error: boolean;
909
913
  * status?: number;
910
914
  * error?: Error;
@@ -922,7 +926,6 @@ async function load_node({
922
926
  $session,
923
927
  stuff,
924
928
  prerender_enabled,
925
- is_leaf,
926
929
  is_error,
927
930
  status,
928
931
  error
@@ -1182,16 +1185,16 @@ async function load_node({
1182
1185
  }
1183
1186
 
1184
1187
  loaded = await module.load.call(null, load_input);
1188
+
1189
+ if (!loaded) {
1190
+ throw new Error(`load function must return a value${options.dev ? ` (${node.entry})` : ''}`);
1191
+ }
1185
1192
  } else {
1186
1193
  loaded = {};
1187
1194
  }
1188
1195
 
1189
- // if leaf node (i.e. page component) has a load function
1190
- // that returns nothing, we fall through to the next one
1191
- if (!loaded && is_leaf && !is_error) return;
1192
-
1193
- if (!loaded) {
1194
- throw new Error(`${node.entry} - load must return a value except for page fall through`);
1196
+ if (loaded.fallthrough && !is_error) {
1197
+ return;
1195
1198
  }
1196
1199
 
1197
1200
  return {
@@ -1219,9 +1222,18 @@ async function load_node({
1219
1222
  * $session: any;
1220
1223
  * status: number;
1221
1224
  * error: Error;
1225
+ * ssr: boolean;
1222
1226
  * }} opts
1223
1227
  */
1224
- async function respond_with_error({ request, options, state, $session, status, error }) {
1228
+ async function respond_with_error({
1229
+ request,
1230
+ options,
1231
+ state,
1232
+ $session,
1233
+ status,
1234
+ error,
1235
+ ssr
1236
+ }) {
1225
1237
  try {
1226
1238
  const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout
1227
1239
  const default_error = await options.manifest._.nodes[1](); // 1 is always the root error
@@ -1229,8 +1241,7 @@ async function respond_with_error({ request, options, state, $session, status, e
1229
1241
  /** @type {Record<string, string>} */
1230
1242
  const params = {}; // error page has no params
1231
1243
 
1232
- // error pages don't fall through, so we know it's not undefined
1233
- const loaded = /** @type {Loaded} */ (
1244
+ const layout_loaded = /** @type {Loaded} */ (
1234
1245
  await load_node({
1235
1246
  request,
1236
1247
  options,
@@ -1242,46 +1253,42 @@ async function respond_with_error({ request, options, state, $session, status, e
1242
1253
  $session,
1243
1254
  stuff: {},
1244
1255
  prerender_enabled: is_prerender_enabled(options, default_error, state),
1245
- is_leaf: false,
1246
1256
  is_error: false
1247
1257
  })
1248
1258
  );
1249
1259
 
1250
- const branch = [
1251
- loaded,
1252
- /** @type {Loaded} */ (
1253
- await load_node({
1254
- request,
1255
- options,
1256
- state,
1257
- route: null,
1258
- url: request.url,
1259
- params,
1260
- node: default_error,
1261
- $session,
1262
- stuff: loaded ? loaded.stuff : {},
1263
- prerender_enabled: is_prerender_enabled(options, default_error, state),
1264
- is_leaf: false,
1265
- is_error: true,
1266
- status,
1267
- error
1268
- })
1269
- )
1270
- ];
1260
+ const error_loaded = /** @type {Loaded} */ (
1261
+ await load_node({
1262
+ request,
1263
+ options,
1264
+ state,
1265
+ route: null,
1266
+ url: request.url,
1267
+ params,
1268
+ node: default_error,
1269
+ $session,
1270
+ stuff: layout_loaded ? layout_loaded.stuff : {},
1271
+ prerender_enabled: is_prerender_enabled(options, default_error, state),
1272
+ is_error: true,
1273
+ status,
1274
+ error
1275
+ })
1276
+ );
1271
1277
 
1272
1278
  return await render_response({
1273
1279
  options,
1274
1280
  $session,
1275
1281
  page_config: {
1276
1282
  hydrate: options.hydrate,
1277
- router: options.router,
1278
- ssr: options.ssr
1283
+ router: options.router
1279
1284
  },
1285
+ stuff: error_loaded.stuff,
1280
1286
  status,
1281
1287
  error,
1282
- branch,
1288
+ branch: [layout_loaded, error_loaded],
1283
1289
  url: request.url,
1284
- params
1290
+ params,
1291
+ ssr
1285
1292
  });
1286
1293
  } catch (err) {
1287
1294
  const error = coalesce_to_error(err);
@@ -1323,15 +1330,30 @@ function is_prerender_enabled(options, node, state) {
1323
1330
  * $session: any;
1324
1331
  * route: import('types/internal').SSRPage;
1325
1332
  * params: Record<string, string>;
1333
+ * ssr: boolean;
1326
1334
  * }} opts
1327
1335
  * @returns {Promise<ServerResponse | undefined>}
1328
1336
  */
1329
1337
  async function respond$1(opts) {
1330
- const { request, options, state, $session, route } = opts;
1338
+ const { request, options, state, $session, route, ssr } = opts;
1331
1339
 
1332
1340
  /** @type {Array<SSRNode | undefined>} */
1333
1341
  let nodes;
1334
1342
 
1343
+ if (!ssr) {
1344
+ return await render_response({
1345
+ ...opts,
1346
+ branch: [],
1347
+ page_config: {
1348
+ hydrate: true,
1349
+ router: true
1350
+ },
1351
+ status: 200,
1352
+ url: request.url,
1353
+ stuff: {}
1354
+ });
1355
+ }
1356
+
1335
1357
  try {
1336
1358
  nodes = await Promise.all(
1337
1359
  route.a.map((n) => options.manifest._.nodes[n] && options.manifest._.nodes[n]())
@@ -1347,7 +1369,8 @@ async function respond$1(opts) {
1347
1369
  state,
1348
1370
  $session,
1349
1371
  status: 500,
1350
- error
1372
+ error,
1373
+ ssr
1351
1374
  });
1352
1375
  }
1353
1376
 
@@ -1377,9 +1400,9 @@ async function respond$1(opts) {
1377
1400
  /** @type {string[]} */
1378
1401
  let set_cookie_headers = [];
1379
1402
 
1380
- ssr: if (page_config.ssr) {
1381
- let stuff = {};
1403
+ let stuff = {};
1382
1404
 
1405
+ ssr: if (ssr) {
1383
1406
  for (let i = 0; i < nodes.length; i += 1) {
1384
1407
  const node = nodes[i];
1385
1408
 
@@ -1394,7 +1417,6 @@ async function respond$1(opts) {
1394
1417
  node,
1395
1418
  stuff,
1396
1419
  prerender_enabled: is_prerender_enabled(options, node, state),
1397
- is_leaf: i === nodes.length - 1,
1398
1420
  is_error: false
1399
1421
  });
1400
1422
 
@@ -1443,7 +1465,6 @@ async function respond$1(opts) {
1443
1465
  }
1444
1466
 
1445
1467
  try {
1446
- // there's no fallthough on an error page, so we know it's not undefined
1447
1468
  const error_loaded = /** @type {import('./types').Loaded} */ (
1448
1469
  await load_node({
1449
1470
  ...opts,
@@ -1451,7 +1472,6 @@ async function respond$1(opts) {
1451
1472
  node: error_node,
1452
1473
  stuff: node_loaded.stuff,
1453
1474
  prerender_enabled: is_prerender_enabled(options, error_node, state),
1454
- is_leaf: false,
1455
1475
  is_error: true,
1456
1476
  status,
1457
1477
  error
@@ -1464,6 +1484,7 @@ async function respond$1(opts) {
1464
1484
 
1465
1485
  page_config = get_page_config(error_node.module, options);
1466
1486
  branch = branch.slice(0, j + 1).concat(error_loaded);
1487
+ stuff = { ...node_loaded.stuff, ...error_loaded.stuff };
1467
1488
  break ssr;
1468
1489
  } catch (err) {
1469
1490
  const e = coalesce_to_error(err);
@@ -1485,7 +1506,8 @@ async function respond$1(opts) {
1485
1506
  state,
1486
1507
  $session,
1487
1508
  status,
1488
- error
1509
+ error,
1510
+ ssr
1489
1511
  }),
1490
1512
  set_cookie_headers
1491
1513
  );
@@ -1505,6 +1527,7 @@ async function respond$1(opts) {
1505
1527
  return with_cookies(
1506
1528
  await render_response({
1507
1529
  ...opts,
1530
+ stuff,
1508
1531
  url: request.url,
1509
1532
  page_config,
1510
1533
  status,
@@ -1534,8 +1557,14 @@ async function respond$1(opts) {
1534
1557
  * @param {SSRRenderOptions} options
1535
1558
  */
1536
1559
  function get_page_config(leaf, options) {
1560
+ // TODO remove for 1.0
1561
+ if ('ssr' in leaf) {
1562
+ throw new Error(
1563
+ '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs#hooks-handle'
1564
+ );
1565
+ }
1566
+
1537
1567
  return {
1538
- ssr: 'ssr' in leaf ? !!leaf.ssr : options.ssr,
1539
1568
  router: 'router' in leaf ? !!leaf.router : options.router,
1540
1569
  hydrate: 'hydrate' in leaf ? !!leaf.hydrate : options.hydrate
1541
1570
  };
@@ -1558,9 +1587,10 @@ function with_cookies(response, set_cookie_headers) {
1558
1587
  * @param {RegExpExecArray} match
1559
1588
  * @param {import('types/internal').SSRRenderOptions} options
1560
1589
  * @param {import('types/internal').SSRRenderState} state
1590
+ * @param {boolean} ssr
1561
1591
  * @returns {Promise<import('types/hooks').ServerResponse | undefined>}
1562
1592
  */
1563
- async function render_page(request, route, match, options, state) {
1593
+ async function render_page(request, route, match, options, state, ssr) {
1564
1594
  if (state.initiator === route) {
1565
1595
  // infinite request cycle detected
1566
1596
  return {
@@ -1580,7 +1610,8 @@ async function render_page(request, route, match, options, state) {
1580
1610
  state,
1581
1611
  $session,
1582
1612
  route,
1583
- params
1613
+ params,
1614
+ ssr
1584
1615
  });
1585
1616
 
1586
1617
  if (response) {
@@ -1835,19 +1866,25 @@ async function respond(incoming, options, state = {}) {
1835
1866
  print_error('path', 'pathname');
1836
1867
  print_error('query', 'searchParams');
1837
1868
 
1869
+ let ssr = true;
1870
+
1838
1871
  try {
1839
1872
  return await options.hooks.handle({
1840
1873
  request,
1841
- resolve: async (request) => {
1874
+ resolve: async (request, opts) => {
1875
+ if (opts && 'ssr' in opts) ssr = /** @type {boolean} */ (opts.ssr);
1876
+
1842
1877
  if (state.prerender && state.prerender.fallback) {
1843
1878
  return await render_response({
1844
1879
  url: request.url,
1845
1880
  params: request.params,
1846
1881
  options,
1847
1882
  $session: await options.hooks.getSession(request),
1848
- page_config: { ssr: false, router: true, hydrate: true },
1883
+ page_config: { router: true, hydrate: true },
1884
+ stuff: {},
1849
1885
  status: 200,
1850
- branch: []
1886
+ branch: [],
1887
+ ssr: false
1851
1888
  });
1852
1889
  }
1853
1890
 
@@ -1860,7 +1897,7 @@ async function respond(incoming, options, state = {}) {
1860
1897
  const response =
1861
1898
  route.type === 'endpoint'
1862
1899
  ? await render_endpoint(request, route, match)
1863
- : await render_page(request, route, match, options, state);
1900
+ : await render_page(request, route, match, options, state, ssr);
1864
1901
 
1865
1902
  if (response) {
1866
1903
  // inject ETags for 200 responses
@@ -1900,7 +1937,8 @@ async function respond(incoming, options, state = {}) {
1900
1937
  state,
1901
1938
  $session,
1902
1939
  status: 404,
1903
- error: new Error(`Not found: ${request.url.pathname}`)
1940
+ error: new Error(`Not found: ${request.url.pathname}`),
1941
+ ssr
1904
1942
  });
1905
1943
  }
1906
1944
  }
@@ -1918,7 +1956,8 @@ async function respond(incoming, options, state = {}) {
1918
1956
  state,
1919
1957
  $session,
1920
1958
  status: 500,
1921
- error
1959
+ error,
1960
+ ssr
1922
1961
  });
1923
1962
  } catch (/** @type {unknown} */ e) {
1924
1963
  const error = coalesce_to_error(e);
@@ -2,8 +2,6 @@ import { getContext } from 'svelte';
2
2
  import { browser } from './env.js';
3
3
  import '../env.js';
4
4
 
5
- const ssr = !browser;
6
-
7
5
  // TODO remove this (for 1.0? after 1.0?)
8
6
  let warned = false;
9
7
  function stores() {
@@ -59,9 +57,9 @@ const navigating = {
59
57
  /** @param {string} verb */
60
58
  const throw_error = (verb) => {
61
59
  throw new Error(
62
- ssr
63
- ? `Can only ${verb} session store in browser`
64
- : `Cannot ${verb} session store before subscribing`
60
+ browser
61
+ ? `Cannot ${verb} session store before subscribing`
62
+ : `Can only ${verb} session store in browser`
65
63
  );
66
64
  };
67
65
 
@@ -70,7 +68,7 @@ const session = {
70
68
  subscribe(fn) {
71
69
  const store = getStores().session;
72
70
 
73
- if (!ssr) {
71
+ if (browser) {
74
72
  session.set = store.set;
75
73
  session.update = store.update;
76
74
  }
@@ -168,6 +168,10 @@ class Router {
168
168
  // Call `pushState` to add url to history so going back works.
169
169
  // Also make a delay, otherwise the browser default behaviour would not kick in
170
170
  setTimeout(() => history.pushState({}, '', url.href));
171
+ const info = this.parse(url);
172
+ if (info) {
173
+ return this.renderer.update(info, [], false);
174
+ }
171
175
  return;
172
176
  }
173
177
 
@@ -543,6 +547,9 @@ class Renderer {
543
547
 
544
548
  let error_args;
545
549
 
550
+ // url.hash is empty when coming from the server
551
+ url.hash = window.location.hash;
552
+
546
553
  try {
547
554
  for (let i = 0; i < nodes.length; i += 1) {
548
555
  const is_leaf = i === nodes.length - 1;
@@ -577,7 +584,14 @@ class Renderer {
577
584
 
578
585
  result = error_args
579
586
  ? await this._load_error(error_args)
580
- : await this._get_navigation_result_from_branch({ url, params, branch, status, error });
587
+ : await this._get_navigation_result_from_branch({
588
+ url,
589
+ params,
590
+ stuff,
591
+ branch,
592
+ status,
593
+ error
594
+ });
581
595
  } catch (e) {
582
596
  if (error) throw e;
583
597
 
@@ -801,12 +815,13 @@ class Renderer {
801
815
  * @param {{
802
816
  * url: URL;
803
817
  * params: Record<string, string>;
818
+ * stuff: Record<string, any>;
804
819
  * branch: Array<import('./types').BranchNode | undefined>;
805
820
  * status: number;
806
821
  * error?: Error;
807
822
  * }} opts
808
823
  */
809
- async _get_navigation_result_from_branch({ url, params, branch, status, error }) {
824
+ async _get_navigation_result_from_branch({ url, params, stuff, branch, status, error }) {
810
825
  const filtered = /** @type {import('./types').BranchNode[] } */ (branch.filter(Boolean));
811
826
  const redirect = filtered.find((f) => f.loaded && f.loaded.redirect);
812
827
 
@@ -830,7 +845,7 @@ class Renderer {
830
845
  }
831
846
 
832
847
  if (!this.current.url || url.href !== this.current.url.href) {
833
- result.props.page = { url, params, status, error };
848
+ result.props.page = { url, params, status, error, stuff };
834
849
 
835
850
  // TODO remove this for 1.0
836
851
  /**
@@ -963,8 +978,9 @@ class Renderer {
963
978
 
964
979
  const loaded = await module.load.call(null, load_input);
965
980
 
966
- // if the page component returns nothing from load, fall through
967
- if (!loaded) return;
981
+ if (!loaded) {
982
+ throw new Error('load function must return a value');
983
+ }
968
984
 
969
985
  node.loaded = normalize(loaded);
970
986
  if (node.loaded.stuff) node.stuff = node.loaded.stuff;
@@ -1041,9 +1057,10 @@ class Renderer {
1041
1057
  stuff
1042
1058
  });
1043
1059
 
1044
- const is_leaf = i === a.length - 1;
1045
-
1046
1060
  if (node && node.loaded) {
1061
+ if (node.loaded.fallthrough) {
1062
+ return;
1063
+ }
1047
1064
  if (node.loaded.error) {
1048
1065
  status = node.loaded.status;
1049
1066
  error = node.loaded.error;
@@ -1060,10 +1077,6 @@ class Renderer {
1060
1077
  if (node.loaded.stuff) {
1061
1078
  stuff_changed = true;
1062
1079
  }
1063
- } else if (is_leaf && module.load) {
1064
- // if the leaf node has a `load` function
1065
- // that returns nothing, fall through
1066
- return;
1067
1080
  }
1068
1081
  } else {
1069
1082
  node = previous;
@@ -1099,6 +1112,13 @@ class Renderer {
1099
1112
  continue;
1100
1113
  }
1101
1114
 
1115
+ if (error_loaded && error_loaded.loaded && error_loaded.loaded.stuff) {
1116
+ stuff = {
1117
+ ...stuff,
1118
+ ...error_loaded.loaded.stuff
1119
+ };
1120
+ }
1121
+
1102
1122
  branch = branch.slice(0, j + 1).concat(error_loaded);
1103
1123
  break load;
1104
1124
  } catch (e) {
@@ -1124,7 +1144,14 @@ class Renderer {
1124
1144
  }
1125
1145
  }
1126
1146
 
1127
- return await this._get_navigation_result_from_branch({ url, params, branch, status, error });
1147
+ return await this._get_navigation_result_from_branch({
1148
+ url,
1149
+ params,
1150
+ stuff,
1151
+ branch,
1152
+ status,
1153
+ error
1154
+ });
1128
1155
  }
1129
1156
 
1130
1157
  /**
@@ -1144,20 +1171,26 @@ class Renderer {
1144
1171
  params,
1145
1172
  stuff: {}
1146
1173
  });
1174
+ const error_node = await this._load_node({
1175
+ status,
1176
+ error,
1177
+ module: await this.fallback[1],
1178
+ url,
1179
+ params,
1180
+ stuff: (node && node.loaded && node.loaded.stuff) || {}
1181
+ });
1147
1182
 
1148
- const branch = [
1149
- node,
1150
- await this._load_node({
1151
- status,
1152
- error,
1153
- module: await this.fallback[1],
1154
- url,
1155
- params,
1156
- stuff: (node && node.loaded && node.loaded.stuff) || {}
1157
- })
1158
- ];
1183
+ const branch = [node, error_node];
1184
+ const stuff = { ...node?.loaded?.stuff, ...error_node?.loaded?.stuff };
1159
1185
 
1160
- return await this._get_navigation_result_from_branch({ url, params, branch, status, error });
1186
+ return await this._get_navigation_result_from_branch({
1187
+ url,
1188
+ params,
1189
+ stuff,
1190
+ branch,
1191
+ status,
1192
+ error
1193
+ });
1161
1194
  }
1162
1195
  }
1163
1196