@sveltejs/kit 1.0.0-next.220 → 1.0.0-next.224

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) {
@@ -1818,6 +1849,28 @@ async function respond(incoming, options, state = {}) {
1818
1849
  locals: {}
1819
1850
  };
1820
1851
 
1852
+ const { parameter, allowed } = options.method_override;
1853
+ const method_override = incoming.url.searchParams.get(parameter)?.toUpperCase();
1854
+
1855
+ if (method_override) {
1856
+ if (request.method.toUpperCase() === 'POST') {
1857
+ if (allowed.includes(method_override)) {
1858
+ request.method = method_override;
1859
+ } else {
1860
+ const verb = allowed.length === 0 ? 'enabled' : 'allowed';
1861
+ const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs#configuration-methodoverride`;
1862
+
1863
+ return {
1864
+ status: 400,
1865
+ headers: {},
1866
+ body
1867
+ };
1868
+ }
1869
+ } else {
1870
+ throw new Error(`${parameter}=${method_override} is only allowed with POST requests`);
1871
+ }
1872
+ }
1873
+
1821
1874
  // TODO remove this for 1.0
1822
1875
  /**
1823
1876
  * @param {string} property
@@ -1835,19 +1888,25 @@ async function respond(incoming, options, state = {}) {
1835
1888
  print_error('path', 'pathname');
1836
1889
  print_error('query', 'searchParams');
1837
1890
 
1891
+ let ssr = true;
1892
+
1838
1893
  try {
1839
1894
  return await options.hooks.handle({
1840
1895
  request,
1841
- resolve: async (request) => {
1896
+ resolve: async (request, opts) => {
1897
+ if (opts && 'ssr' in opts) ssr = /** @type {boolean} */ (opts.ssr);
1898
+
1842
1899
  if (state.prerender && state.prerender.fallback) {
1843
1900
  return await render_response({
1844
1901
  url: request.url,
1845
1902
  params: request.params,
1846
1903
  options,
1847
1904
  $session: await options.hooks.getSession(request),
1848
- page_config: { ssr: false, router: true, hydrate: true },
1905
+ page_config: { router: true, hydrate: true },
1906
+ stuff: {},
1849
1907
  status: 200,
1850
- branch: []
1908
+ branch: [],
1909
+ ssr: false
1851
1910
  });
1852
1911
  }
1853
1912
 
@@ -1860,7 +1919,7 @@ async function respond(incoming, options, state = {}) {
1860
1919
  const response =
1861
1920
  route.type === 'endpoint'
1862
1921
  ? await render_endpoint(request, route, match)
1863
- : await render_page(request, route, match, options, state);
1922
+ : await render_page(request, route, match, options, state, ssr);
1864
1923
 
1865
1924
  if (response) {
1866
1925
  // inject ETags for 200 responses
@@ -1900,7 +1959,8 @@ async function respond(incoming, options, state = {}) {
1900
1959
  state,
1901
1960
  $session,
1902
1961
  status: 404,
1903
- error: new Error(`Not found: ${request.url.pathname}`)
1962
+ error: new Error(`Not found: ${request.url.pathname}`),
1963
+ ssr
1904
1964
  });
1905
1965
  }
1906
1966
  }
@@ -1918,7 +1978,8 @@ async function respond(incoming, options, state = {}) {
1918
1978
  state,
1919
1979
  $session,
1920
1980
  status: 500,
1921
- error
1981
+ error,
1982
+ ssr
1922
1983
  });
1923
1984
  } catch (/** @type {unknown} */ e) {
1924
1985
  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
  }
@@ -584,7 +584,14 @@ class Renderer {
584
584
 
585
585
  result = error_args
586
586
  ? await this._load_error(error_args)
587
- : 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
+ });
588
595
  } catch (e) {
589
596
  if (error) throw e;
590
597
 
@@ -808,12 +815,13 @@ class Renderer {
808
815
  * @param {{
809
816
  * url: URL;
810
817
  * params: Record<string, string>;
818
+ * stuff: Record<string, any>;
811
819
  * branch: Array<import('./types').BranchNode | undefined>;
812
820
  * status: number;
813
821
  * error?: Error;
814
822
  * }} opts
815
823
  */
816
- async _get_navigation_result_from_branch({ url, params, branch, status, error }) {
824
+ async _get_navigation_result_from_branch({ url, params, stuff, branch, status, error }) {
817
825
  const filtered = /** @type {import('./types').BranchNode[] } */ (branch.filter(Boolean));
818
826
  const redirect = filtered.find((f) => f.loaded && f.loaded.redirect);
819
827
 
@@ -837,7 +845,7 @@ class Renderer {
837
845
  }
838
846
 
839
847
  if (!this.current.url || url.href !== this.current.url.href) {
840
- result.props.page = { url, params, status, error };
848
+ result.props.page = { url, params, status, error, stuff };
841
849
 
842
850
  // TODO remove this for 1.0
843
851
  /**
@@ -970,8 +978,9 @@ class Renderer {
970
978
 
971
979
  const loaded = await module.load.call(null, load_input);
972
980
 
973
- // if the page component returns nothing from load, fall through
974
- if (!loaded) return;
981
+ if (!loaded) {
982
+ throw new Error('load function must return a value');
983
+ }
975
984
 
976
985
  node.loaded = normalize(loaded);
977
986
  if (node.loaded.stuff) node.stuff = node.loaded.stuff;
@@ -1048,9 +1057,10 @@ class Renderer {
1048
1057
  stuff
1049
1058
  });
1050
1059
 
1051
- const is_leaf = i === a.length - 1;
1052
-
1053
1060
  if (node && node.loaded) {
1061
+ if (node.loaded.fallthrough) {
1062
+ return;
1063
+ }
1054
1064
  if (node.loaded.error) {
1055
1065
  status = node.loaded.status;
1056
1066
  error = node.loaded.error;
@@ -1067,10 +1077,6 @@ class Renderer {
1067
1077
  if (node.loaded.stuff) {
1068
1078
  stuff_changed = true;
1069
1079
  }
1070
- } else if (is_leaf && module.load) {
1071
- // if the leaf node has a `load` function
1072
- // that returns nothing, fall through
1073
- return;
1074
1080
  }
1075
1081
  } else {
1076
1082
  node = previous;
@@ -1106,6 +1112,13 @@ class Renderer {
1106
1112
  continue;
1107
1113
  }
1108
1114
 
1115
+ if (error_loaded && error_loaded.loaded && error_loaded.loaded.stuff) {
1116
+ stuff = {
1117
+ ...stuff,
1118
+ ...error_loaded.loaded.stuff
1119
+ };
1120
+ }
1121
+
1109
1122
  branch = branch.slice(0, j + 1).concat(error_loaded);
1110
1123
  break load;
1111
1124
  } catch (e) {
@@ -1131,7 +1144,14 @@ class Renderer {
1131
1144
  }
1132
1145
  }
1133
1146
 
1134
- 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
+ });
1135
1155
  }
1136
1156
 
1137
1157
  /**
@@ -1151,20 +1171,26 @@ class Renderer {
1151
1171
  params,
1152
1172
  stuff: {}
1153
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
+ });
1154
1182
 
1155
- const branch = [
1156
- node,
1157
- await this._load_node({
1158
- status,
1159
- error,
1160
- module: await this.fallback[1],
1161
- url,
1162
- params,
1163
- stuff: (node && node.loaded && node.loaded.stuff) || {}
1164
- })
1165
- ];
1183
+ const branch = [node, error_node];
1184
+ const stuff = { ...node?.loaded?.stuff, ...error_node?.loaded?.stuff };
1166
1185
 
1167
- 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
+ });
1168
1194
  }
1169
1195
  }
1170
1196