@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/dist/cli.js CHANGED
@@ -575,7 +575,14 @@ const options = object(
575
575
  files: fun((filename) => !/\.DS_STORE/.test(filename))
576
576
  }),
577
577
 
578
- ssr: boolean(true),
578
+ // TODO remove this for 1.0
579
+ ssr: validate(null, (input) => {
580
+ if (input !== undefined) {
581
+ throw new Error(
582
+ 'config.kit.ssr has been removed — use the handle hook instead: https://kit.svelte.dev/docs#hooks-handle'
583
+ );
584
+ }
585
+ }),
579
586
 
580
587
  target: string(null),
581
588
 
@@ -846,7 +853,7 @@ async function launch(port, https) {
846
853
  exec(`${cmd} ${https ? 'https' : 'http'}://localhost:${port}`);
847
854
  }
848
855
 
849
- const prog = sade('svelte-kit').version('1.0.0-next.219');
856
+ const prog = sade('svelte-kit').version('1.0.0-next.222');
850
857
 
851
858
  prog
852
859
  .command('dev')
@@ -998,7 +1005,7 @@ async function check_port(port) {
998
1005
  function welcome({ port, host, https, open, loose, allow, cwd }) {
999
1006
  if (open) launch(port, https);
1000
1007
 
1001
- console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.219'}\n`));
1008
+ console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.222'}\n`));
1002
1009
 
1003
1010
  const protocol = https ? 'https:' : 'http:';
1004
1011
  const exposed = typeof host !== 'undefined' && host !== 'localhost' && host !== '127.0.0.1';
package/dist/ssr.js CHANGED
@@ -94,13 +94,14 @@ async function render_endpoint(request, route, match) {
94
94
  const response = await handler(request);
95
95
  const preface = `Invalid response from route ${request.url.pathname}`;
96
96
 
97
- if (!response) {
98
- return;
99
- }
100
97
  if (typeof response !== 'object') {
101
98
  return error(`${preface}: expected an object, got ${typeof response}`);
102
99
  }
103
100
 
101
+ if (response.fallthrough) {
102
+ return;
103
+ }
104
+
104
105
  let { status = 200, body, headers = {} } = response;
105
106
 
106
107
  headers = lowercase_keys(headers);
@@ -512,11 +513,13 @@ function escape(str, dict, unicode_encoder) {
512
513
  * branch: Array<import('./types').Loaded>;
513
514
  * options: import('types/internal').SSRRenderOptions;
514
515
  * $session: any;
515
- * page_config: { hydrate: boolean, router: boolean, ssr: boolean };
516
+ * page_config: { hydrate: boolean, router: boolean };
516
517
  * status: number;
517
- * error?: Error,
518
+ * error?: Error;
518
519
  * url: URL;
519
- * params: Record<string, string>
520
+ * params: Record<string, string>;
521
+ * ssr: boolean;
522
+ * stuff: Record<string, any>;
520
523
  * }} opts
521
524
  */
522
525
  async function render_response({
@@ -527,7 +530,9 @@ async function render_response({
527
530
  status,
528
531
  error,
529
532
  url,
530
- params
533
+ params,
534
+ ssr,
535
+ stuff
531
536
  }) {
532
537
  const css = new Set(options.manifest._.entry.css);
533
538
  const js = new Set(options.manifest._.entry.js);
@@ -545,7 +550,7 @@ async function render_response({
545
550
  error.stack = options.get_stack(error);
546
551
  }
547
552
 
548
- if (page_config.ssr) {
553
+ if (ssr) {
549
554
  branch.forEach(({ node, loaded, fetched, uses_credentials }) => {
550
555
  if (node.css) node.css.forEach((url) => css.add(url));
551
556
  if (node.js) node.js.forEach((url) => js.add(url));
@@ -568,7 +573,7 @@ async function render_response({
568
573
  navigating: writable(null),
569
574
  session
570
575
  },
571
- page: { url, params, status, error },
576
+ page: { url, params, status, error, stuff },
572
577
  components: branch.map(({ node }) => node.module.default)
573
578
  };
574
579
 
@@ -648,9 +653,9 @@ async function render_response({
648
653
  throw new Error(`Failed to serialize session data: ${error.message}`);
649
654
  })},
650
655
  route: ${!!page_config.router},
651
- spa: ${!page_config.ssr},
656
+ spa: ${!ssr},
652
657
  trailing_slash: ${s(options.trailing_slash)},
653
- hydrate: ${page_config.ssr && page_config.hydrate ? `{
658
+ hydrate: ${ssr && page_config.hydrate ? `{
654
659
  status: ${status},
655
660
  error: ${serialize_error(error)},
656
661
  nodes: [
@@ -834,7 +839,6 @@ function normalize(loaded) {
834
839
  * $session: any;
835
840
  * stuff: Record<string, any>;
836
841
  * prerender_enabled: boolean;
837
- * is_leaf: boolean;
838
842
  * is_error: boolean;
839
843
  * status?: number;
840
844
  * error?: Error;
@@ -852,7 +856,6 @@ async function load_node({
852
856
  $session,
853
857
  stuff,
854
858
  prerender_enabled,
855
- is_leaf,
856
859
  is_error,
857
860
  status,
858
861
  error
@@ -1112,16 +1115,16 @@ async function load_node({
1112
1115
  }
1113
1116
 
1114
1117
  loaded = await module.load.call(null, load_input);
1118
+
1119
+ if (!loaded) {
1120
+ throw new Error(`load function must return a value${options.dev ? ` (${node.entry})` : ''}`);
1121
+ }
1115
1122
  } else {
1116
1123
  loaded = {};
1117
1124
  }
1118
1125
 
1119
- // if leaf node (i.e. page component) has a load function
1120
- // that returns nothing, we fall through to the next one
1121
- if (!loaded && is_leaf && !is_error) return;
1122
-
1123
- if (!loaded) {
1124
- throw new Error(`${node.entry} - load must return a value except for page fall through`);
1126
+ if (loaded.fallthrough && !is_error) {
1127
+ return;
1125
1128
  }
1126
1129
 
1127
1130
  return {
@@ -1149,9 +1152,18 @@ async function load_node({
1149
1152
  * $session: any;
1150
1153
  * status: number;
1151
1154
  * error: Error;
1155
+ * ssr: boolean;
1152
1156
  * }} opts
1153
1157
  */
1154
- async function respond_with_error({ request, options, state, $session, status, error }) {
1158
+ async function respond_with_error({
1159
+ request,
1160
+ options,
1161
+ state,
1162
+ $session,
1163
+ status,
1164
+ error,
1165
+ ssr
1166
+ }) {
1155
1167
  try {
1156
1168
  const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout
1157
1169
  const default_error = await options.manifest._.nodes[1](); // 1 is always the root error
@@ -1159,8 +1171,7 @@ async function respond_with_error({ request, options, state, $session, status, e
1159
1171
  /** @type {Record<string, string>} */
1160
1172
  const params = {}; // error page has no params
1161
1173
 
1162
- // error pages don't fall through, so we know it's not undefined
1163
- const loaded = /** @type {Loaded} */ (
1174
+ const layout_loaded = /** @type {Loaded} */ (
1164
1175
  await load_node({
1165
1176
  request,
1166
1177
  options,
@@ -1172,46 +1183,42 @@ async function respond_with_error({ request, options, state, $session, status, e
1172
1183
  $session,
1173
1184
  stuff: {},
1174
1185
  prerender_enabled: is_prerender_enabled(options, default_error, state),
1175
- is_leaf: false,
1176
1186
  is_error: false
1177
1187
  })
1178
1188
  );
1179
1189
 
1180
- const branch = [
1181
- loaded,
1182
- /** @type {Loaded} */ (
1183
- await load_node({
1184
- request,
1185
- options,
1186
- state,
1187
- route: null,
1188
- url: request.url,
1189
- params,
1190
- node: default_error,
1191
- $session,
1192
- stuff: loaded ? loaded.stuff : {},
1193
- prerender_enabled: is_prerender_enabled(options, default_error, state),
1194
- is_leaf: false,
1195
- is_error: true,
1196
- status,
1197
- error
1198
- })
1199
- )
1200
- ];
1190
+ const error_loaded = /** @type {Loaded} */ (
1191
+ await load_node({
1192
+ request,
1193
+ options,
1194
+ state,
1195
+ route: null,
1196
+ url: request.url,
1197
+ params,
1198
+ node: default_error,
1199
+ $session,
1200
+ stuff: layout_loaded ? layout_loaded.stuff : {},
1201
+ prerender_enabled: is_prerender_enabled(options, default_error, state),
1202
+ is_error: true,
1203
+ status,
1204
+ error
1205
+ })
1206
+ );
1201
1207
 
1202
1208
  return await render_response({
1203
1209
  options,
1204
1210
  $session,
1205
1211
  page_config: {
1206
1212
  hydrate: options.hydrate,
1207
- router: options.router,
1208
- ssr: options.ssr
1213
+ router: options.router
1209
1214
  },
1215
+ stuff: error_loaded.stuff,
1210
1216
  status,
1211
1217
  error,
1212
- branch,
1218
+ branch: [layout_loaded, error_loaded],
1213
1219
  url: request.url,
1214
- params
1220
+ params,
1221
+ ssr
1215
1222
  });
1216
1223
  } catch (err) {
1217
1224
  const error = coalesce_to_error(err);
@@ -1253,15 +1260,30 @@ function is_prerender_enabled(options, node, state) {
1253
1260
  * $session: any;
1254
1261
  * route: import('types/internal').SSRPage;
1255
1262
  * params: Record<string, string>;
1263
+ * ssr: boolean;
1256
1264
  * }} opts
1257
1265
  * @returns {Promise<ServerResponse | undefined>}
1258
1266
  */
1259
1267
  async function respond$1(opts) {
1260
- const { request, options, state, $session, route } = opts;
1268
+ const { request, options, state, $session, route, ssr } = opts;
1261
1269
 
1262
1270
  /** @type {Array<SSRNode | undefined>} */
1263
1271
  let nodes;
1264
1272
 
1273
+ if (!ssr) {
1274
+ return await render_response({
1275
+ ...opts,
1276
+ branch: [],
1277
+ page_config: {
1278
+ hydrate: true,
1279
+ router: true
1280
+ },
1281
+ status: 200,
1282
+ url: request.url,
1283
+ stuff: {}
1284
+ });
1285
+ }
1286
+
1265
1287
  try {
1266
1288
  nodes = await Promise.all(
1267
1289
  route.a.map((n) => options.manifest._.nodes[n] && options.manifest._.nodes[n]())
@@ -1277,7 +1299,8 @@ async function respond$1(opts) {
1277
1299
  state,
1278
1300
  $session,
1279
1301
  status: 500,
1280
- error
1302
+ error,
1303
+ ssr
1281
1304
  });
1282
1305
  }
1283
1306
 
@@ -1307,9 +1330,9 @@ async function respond$1(opts) {
1307
1330
  /** @type {string[]} */
1308
1331
  let set_cookie_headers = [];
1309
1332
 
1310
- ssr: if (page_config.ssr) {
1311
- let stuff = {};
1333
+ let stuff = {};
1312
1334
 
1335
+ ssr: if (ssr) {
1313
1336
  for (let i = 0; i < nodes.length; i += 1) {
1314
1337
  const node = nodes[i];
1315
1338
 
@@ -1324,7 +1347,6 @@ async function respond$1(opts) {
1324
1347
  node,
1325
1348
  stuff,
1326
1349
  prerender_enabled: is_prerender_enabled(options, node, state),
1327
- is_leaf: i === nodes.length - 1,
1328
1350
  is_error: false
1329
1351
  });
1330
1352
 
@@ -1373,7 +1395,6 @@ async function respond$1(opts) {
1373
1395
  }
1374
1396
 
1375
1397
  try {
1376
- // there's no fallthough on an error page, so we know it's not undefined
1377
1398
  const error_loaded = /** @type {import('./types').Loaded} */ (
1378
1399
  await load_node({
1379
1400
  ...opts,
@@ -1381,7 +1402,6 @@ async function respond$1(opts) {
1381
1402
  node: error_node,
1382
1403
  stuff: node_loaded.stuff,
1383
1404
  prerender_enabled: is_prerender_enabled(options, error_node, state),
1384
- is_leaf: false,
1385
1405
  is_error: true,
1386
1406
  status,
1387
1407
  error
@@ -1394,6 +1414,7 @@ async function respond$1(opts) {
1394
1414
 
1395
1415
  page_config = get_page_config(error_node.module, options);
1396
1416
  branch = branch.slice(0, j + 1).concat(error_loaded);
1417
+ stuff = { ...node_loaded.stuff, ...error_loaded.stuff };
1397
1418
  break ssr;
1398
1419
  } catch (err) {
1399
1420
  const e = coalesce_to_error(err);
@@ -1415,7 +1436,8 @@ async function respond$1(opts) {
1415
1436
  state,
1416
1437
  $session,
1417
1438
  status,
1418
- error
1439
+ error,
1440
+ ssr
1419
1441
  }),
1420
1442
  set_cookie_headers
1421
1443
  );
@@ -1435,6 +1457,7 @@ async function respond$1(opts) {
1435
1457
  return with_cookies(
1436
1458
  await render_response({
1437
1459
  ...opts,
1460
+ stuff,
1438
1461
  url: request.url,
1439
1462
  page_config,
1440
1463
  status,
@@ -1464,8 +1487,14 @@ async function respond$1(opts) {
1464
1487
  * @param {SSRRenderOptions} options
1465
1488
  */
1466
1489
  function get_page_config(leaf, options) {
1490
+ // TODO remove for 1.0
1491
+ if ('ssr' in leaf) {
1492
+ throw new Error(
1493
+ '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs#hooks-handle'
1494
+ );
1495
+ }
1496
+
1467
1497
  return {
1468
- ssr: 'ssr' in leaf ? !!leaf.ssr : options.ssr,
1469
1498
  router: 'router' in leaf ? !!leaf.router : options.router,
1470
1499
  hydrate: 'hydrate' in leaf ? !!leaf.hydrate : options.hydrate
1471
1500
  };
@@ -1488,9 +1517,10 @@ function with_cookies(response, set_cookie_headers) {
1488
1517
  * @param {RegExpExecArray} match
1489
1518
  * @param {import('types/internal').SSRRenderOptions} options
1490
1519
  * @param {import('types/internal').SSRRenderState} state
1520
+ * @param {boolean} ssr
1491
1521
  * @returns {Promise<import('types/hooks').ServerResponse | undefined>}
1492
1522
  */
1493
- async function render_page(request, route, match, options, state) {
1523
+ async function render_page(request, route, match, options, state, ssr) {
1494
1524
  if (state.initiator === route) {
1495
1525
  // infinite request cycle detected
1496
1526
  return {
@@ -1510,7 +1540,8 @@ async function render_page(request, route, match, options, state) {
1510
1540
  state,
1511
1541
  $session,
1512
1542
  route,
1513
- params
1543
+ params,
1544
+ ssr
1514
1545
  });
1515
1546
 
1516
1547
  if (response) {
@@ -1765,19 +1796,25 @@ async function respond(incoming, options, state = {}) {
1765
1796
  print_error('path', 'pathname');
1766
1797
  print_error('query', 'searchParams');
1767
1798
 
1799
+ let ssr = true;
1800
+
1768
1801
  try {
1769
1802
  return await options.hooks.handle({
1770
1803
  request,
1771
- resolve: async (request) => {
1804
+ resolve: async (request, opts) => {
1805
+ if (opts && 'ssr' in opts) ssr = /** @type {boolean} */ (opts.ssr);
1806
+
1772
1807
  if (state.prerender && state.prerender.fallback) {
1773
1808
  return await render_response({
1774
1809
  url: request.url,
1775
1810
  params: request.params,
1776
1811
  options,
1777
1812
  $session: await options.hooks.getSession(request),
1778
- page_config: { ssr: false, router: true, hydrate: true },
1813
+ page_config: { router: true, hydrate: true },
1814
+ stuff: {},
1779
1815
  status: 200,
1780
- branch: []
1816
+ branch: [],
1817
+ ssr: false
1781
1818
  });
1782
1819
  }
1783
1820
 
@@ -1790,7 +1827,7 @@ async function respond(incoming, options, state = {}) {
1790
1827
  const response =
1791
1828
  route.type === 'endpoint'
1792
1829
  ? await render_endpoint(request, route, match)
1793
- : await render_page(request, route, match, options, state);
1830
+ : await render_page(request, route, match, options, state, ssr);
1794
1831
 
1795
1832
  if (response) {
1796
1833
  // inject ETags for 200 responses
@@ -1830,7 +1867,8 @@ async function respond(incoming, options, state = {}) {
1830
1867
  state,
1831
1868
  $session,
1832
1869
  status: 404,
1833
- error: new Error(`Not found: ${request.url.pathname}`)
1870
+ error: new Error(`Not found: ${request.url.pathname}`),
1871
+ ssr
1834
1872
  });
1835
1873
  }
1836
1874
  }
@@ -1848,7 +1886,8 @@ async function respond(incoming, options, state = {}) {
1848
1886
  state,
1849
1887
  $session,
1850
1888
  status: 500,
1851
- error
1889
+ error,
1890
+ ssr
1852
1891
  });
1853
1892
  } catch (/** @type {unknown} */ e) {
1854
1893
  const error = coalesce_to_error(e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.219",
3
+ "version": "1.0.0-next.222",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -110,6 +110,7 @@ declare module '$app/stores' {
110
110
  export const page: Readable<{
111
111
  url: URL;
112
112
  params: Record<string, string>;
113
+ stuff: Record<string, any>;
113
114
  status: number;
114
115
  error: Error | null;
115
116
  }>;
package/types/config.d.ts CHANGED
@@ -154,7 +154,6 @@ export interface Config {
154
154
  register?: boolean;
155
155
  files?: (filepath: string) => boolean;
156
156
  };
157
- ssr?: boolean;
158
157
  target?: string;
159
158
  trailingSlash?: TrailingSlash;
160
159
  vite?: ViteConfig | (() => ViteConfig);
@@ -1,5 +1,5 @@
1
1
  import { ServerRequest } from './hooks';
2
- import { JSONString, MaybePromise, ResponseHeaders } from './helper';
2
+ import { JSONString, MaybePromise, ResponseHeaders, Either, Fallthrough } from './helper';
3
3
 
4
4
  type DefaultBody = JSONString | Uint8Array;
5
5
 
@@ -14,5 +14,7 @@ export interface RequestHandler<
14
14
  Input = unknown,
15
15
  Output extends DefaultBody = DefaultBody
16
16
  > {
17
- (request: ServerRequest<Locals, Input>): MaybePromise<void | EndpointOutput<Output>>;
17
+ (request: ServerRequest<Locals, Input>): MaybePromise<
18
+ Either<EndpointOutput<Output>, Fallthrough>
19
+ >;
18
20
  }
package/types/helper.d.ts CHANGED
@@ -39,3 +39,15 @@ export type RecursiveRequired<T> = {
39
39
  ? Extract<T[K], Function> // only take the Function type.
40
40
  : T[K]; // Use the exact type for everything else
41
41
  };
42
+
43
+ type Only<T, U> = {
44
+ [P in keyof T]: T[P];
45
+ } & {
46
+ [P in keyof U]?: never;
47
+ };
48
+
49
+ export type Either<T, U> = Only<T, U> | Only<U, T>;
50
+
51
+ export interface Fallthrough {
52
+ fallthrough: true;
53
+ }
package/types/hooks.d.ts CHANGED
@@ -23,10 +23,14 @@ export interface GetSession<Locals = Record<string, any>, Body = unknown, Sessio
23
23
  (request: ServerRequest<Locals, Body>): MaybePromise<Session>;
24
24
  }
25
25
 
26
+ export interface ResolveOpts {
27
+ ssr?: boolean;
28
+ }
29
+
26
30
  export interface Handle<Locals = Record<string, any>, Body = unknown> {
27
31
  (input: {
28
32
  request: ServerRequest<Locals, Body>;
29
- resolve(request: ServerRequest<Locals, Body>): MaybePromise<ServerResponse>;
33
+ resolve(request: ServerRequest<Locals, Body>, opts?: ResolveOpts): MaybePromise<ServerResponse>;
30
34
  }): MaybePromise<ServerResponse>;
31
35
  }
32
36
 
@@ -35,7 +39,10 @@ export interface Handle<Locals = Record<string, any>, Body = unknown> {
35
39
  export interface InternalHandle<Locals = Record<string, any>, Body = unknown> {
36
40
  (input: {
37
41
  request: ServerRequest<Locals, Body>;
38
- resolve(request: ServerRequest<Locals, Body>): MaybePromise<ServerResponse | undefined>;
42
+ resolve(
43
+ request: ServerRequest<Locals, Body>,
44
+ opts?: ResolveOpts
45
+ ): MaybePromise<ServerResponse | undefined>;
39
46
  }): MaybePromise<ServerResponse | undefined>;
40
47
  }
41
48
 
package/types/index.d.ts CHANGED
@@ -13,5 +13,6 @@ export {
13
13
  Handle,
14
14
  HandleError,
15
15
  ServerRequest as Request,
16
- ServerResponse as Response
16
+ ServerResponse as Response,
17
+ ResolveOpts
17
18
  } from './hooks';
@@ -10,6 +10,7 @@ import {
10
10
  ServerResponse
11
11
  } from './hooks';
12
12
  import { Load } from './page';
13
+ import { Either, Fallthrough } from './helper';
13
14
 
14
15
  type PageId = string;
15
16
 
@@ -43,11 +44,9 @@ export interface Logger {
43
44
  }
44
45
 
45
46
  export interface SSRComponent {
46
- ssr?: boolean;
47
47
  router?: boolean;
48
48
  hydrate?: boolean;
49
49
  prerender?: boolean;
50
- preload?: any; // TODO remove for 1.0
51
50
  load: Load;
52
51
  default: {
53
52
  render(props: Record<string, any>): {
@@ -142,7 +141,6 @@ export interface SSRRenderOptions {
142
141
  root: SSRComponent['default'];
143
142
  router: boolean;
144
143
  service_worker?: string;
145
- ssr: boolean;
146
144
  target: string;
147
145
  template({ head, body, assets }: { head: string; body: string; assets: string }): string;
148
146
  trailing_slash: TrailingSlash;
@@ -219,13 +217,16 @@ export interface BuildData {
219
217
  entries: string[];
220
218
  }
221
219
 
222
- export interface NormalizedLoadOutput {
223
- status: number;
224
- error?: Error;
225
- redirect?: string;
226
- props?: Record<string, any> | Promise<Record<string, any>>;
227
- stuff?: Record<string, any>;
228
- maxage?: number;
229
- }
220
+ export type NormalizedLoadOutput = Either<
221
+ {
222
+ status: number;
223
+ error?: Error;
224
+ redirect?: string;
225
+ props?: Record<string, any> | Promise<Record<string, any>>;
226
+ stuff?: Record<string, any>;
227
+ maxage?: number;
228
+ },
229
+ Fallthrough
230
+ >;
230
231
 
231
232
  export type TrailingSlash = 'never' | 'always' | 'ignore';
package/types/page.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { InferValue, MaybePromise, Rec } from './helper';
1
+ import { InferValue, MaybePromise, Rec, Either, Fallthrough } from './helper';
2
2
 
3
3
  export interface LoadInput<
4
4
  PageParams extends Rec<string> = Rec<string>,
@@ -51,10 +51,12 @@ export interface Load<
51
51
  InferValue<Input, 'stuff', Rec>,
52
52
  InferValue<Input, 'session', any>
53
53
  >
54
- ): MaybePromise<void | LoadOutput<
55
- InferValue<Output, 'props', Rec>,
56
- InferValue<Output, 'stuff', Rec>
57
- >>;
54
+ ): MaybePromise<
55
+ Either<
56
+ LoadOutput<InferValue<Output, 'props', Rec>, InferValue<Output, 'stuff', Rec>>,
57
+ Fallthrough
58
+ >
59
+ >;
58
60
  }
59
61
 
60
62
  export interface ErrorLoad<