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

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
@@ -537,12 +537,25 @@ function escape(str, dict, unicode_encoder) {
537
537
 
538
538
  const s = JSON.stringify;
539
539
 
540
+ /** @param {URL} url */
541
+ function create_prerendering_url_proxy(url) {
542
+ return new Proxy(url, {
543
+ get: (target, prop, receiver) => {
544
+ if (prop === 'search' || prop === 'searchParams') {
545
+ throw new Error(`Cannot access url.${prop} on a page with prerendering enabled`);
546
+ }
547
+ return Reflect.get(target, prop, receiver);
548
+ }
549
+ });
550
+ }
551
+
540
552
  // TODO rename this function/module
541
553
 
542
554
  /**
543
555
  * @param {{
544
556
  * branch: Array<import('./types').Loaded>;
545
557
  * options: import('types/internal').SSRRenderOptions;
558
+ * state: import('types/internal').SSRRenderState;
546
559
  * $session: any;
547
560
  * page_config: { hydrate: boolean, router: boolean };
548
561
  * status: number;
@@ -556,6 +569,7 @@ const s = JSON.stringify;
556
569
  async function render_response({
557
570
  branch,
558
571
  options,
572
+ state,
559
573
  $session,
560
574
  page_config,
561
575
  status,
@@ -567,7 +581,8 @@ async function render_response({
567
581
  }) {
568
582
  const css = new Set(options.manifest._.entry.css);
569
583
  const js = new Set(options.manifest._.entry.js);
570
- const styles = new Set();
584
+ /** @type {Map<string, string>} */
585
+ const styles = new Map();
571
586
 
572
587
  /** @type {Array<{ url: string, body: string, json: string }>} */
573
588
  const serialized_data = [];
@@ -585,7 +600,7 @@ async function render_response({
585
600
  branch.forEach(({ node, loaded, fetched, uses_credentials }) => {
586
601
  if (node.css) node.css.forEach((url) => css.add(url));
587
602
  if (node.js) node.js.forEach((url) => js.add(url));
588
- if (node.styles) node.styles.forEach((content) => styles.add(content));
603
+ if (node.styles) Object.entries(node.styles).forEach(([k, v]) => styles.set(k, v));
589
604
 
590
605
  // TODO probably better if `fetched` wasn't populated unless `hydrate`
591
606
  if (fetched && page_config.hydrate) serialized_data.push(...fetched);
@@ -604,7 +619,13 @@ async function render_response({
604
619
  navigating: writable(null),
605
620
  session
606
621
  },
607
- page: { url, params, status, error, stuff },
622
+ page: {
623
+ url: state.prerender ? create_prerendering_url_proxy(url) : url,
624
+ params,
625
+ status,
626
+ error,
627
+ stuff
628
+ },
608
629
  components: branch.map(({ node }) => node.module.default)
609
630
  };
610
631
 
@@ -646,94 +667,80 @@ async function render_response({
646
667
  rendered = { head: '', html: '', css: { code: '', map: null } };
647
668
  }
648
669
 
649
- const include_js = page_config.router || page_config.hydrate;
650
- if (!include_js) js.clear();
651
-
652
- // TODO strip the AMP stuff out of the build if not relevant
653
- const links = options.amp
654
- ? styles.size > 0 || rendered.css.code.length > 0
655
- ? `<style amp-custom>${Array.from(styles).concat(rendered.css.code).join('\n')}</style>`
656
- : ''
657
- : [
658
- // From https://web.dev/priority-hints/:
659
- // Generally, preloads will load in the order the parser gets to them for anything above "Medium" priority
660
- // Thus, we should list CSS first
661
- ...Array.from(css).map((dep) => `<link rel="stylesheet" href="${options.prefix}${dep}">`),
662
- ...Array.from(js).map((dep) => `<link rel="modulepreload" href="${options.prefix}${dep}">`)
663
- ].join('\n\t\t');
664
-
665
- /** @type {string} */
666
- let init = '';
670
+ let { head, html: body } = rendered;
671
+
672
+ const inlined_style = Array.from(styles.values()).join('\n');
667
673
 
668
674
  if (options.amp) {
669
- init = `
675
+ head += `
670
676
  <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style>
671
677
  <noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
672
- <script async src="https://cdn.ampproject.org/v0.js"></script>`;
673
- init += options.service_worker
674
- ? '<script async custom-element="amp-install-serviceworker" src="https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js"></script>'
675
- : '';
676
- } else if (include_js) {
677
- // prettier-ignore
678
- init = `<script type="module">
679
- import { start } from ${s(options.prefix + options.manifest._.entry.file)};
680
- start({
681
- target: ${options.target ? `document.querySelector(${s(options.target)})` : 'document.body'},
682
- paths: ${s(options.paths)},
683
- session: ${try_serialize($session, (error) => {
684
- throw new Error(`Failed to serialize session data: ${error.message}`);
685
- })},
686
- route: ${!!page_config.router},
687
- spa: ${!ssr},
688
- trailing_slash: ${s(options.trailing_slash)},
689
- hydrate: ${ssr && page_config.hydrate ? `{
690
- status: ${status},
691
- error: ${serialize_error(error)},
692
- nodes: [
693
- ${(branch || [])
694
- .map(({ node }) => `import(${s(options.prefix + node.entry)})`)
695
- .join(',\n\t\t\t\t\t\t')}
696
- ],
697
- url: new URL(${s(url.href)}),
698
- params: ${devalue(params)}
699
- }` : 'null'}
700
- });
701
- </script>`;
702
- }
678
+ <script async src="https://cdn.ampproject.org/v0.js"></script>
703
679
 
704
- if (options.service_worker && !options.amp) {
705
- init += `<script>
706
- if ('serviceWorker' in navigator) {
707
- navigator.serviceWorker.register('${options.service_worker}');
708
- }
709
- </script>`;
710
- }
711
-
712
- const head = [
713
- rendered.head,
714
- styles.size && !options.amp
715
- ? `<style data-svelte>${Array.from(styles).join('\n')}</style>`
716
- : '',
717
- links,
718
- init
719
- ].join('\n\n\t\t');
680
+ <style amp-custom>${inlined_style}\n${rendered.css.code}</style>`;
720
681
 
721
- let body = rendered.html;
722
- if (options.amp) {
723
682
  if (options.service_worker) {
683
+ head +=
684
+ '<script async custom-element="amp-install-serviceworker" src="https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js"></script>';
685
+
724
686
  body += `<amp-install-serviceworker src="${options.service_worker}" layout="nodisplay"></amp-install-serviceworker>`;
725
687
  }
726
688
  } else {
727
- body += serialized_data
728
- .map(({ url, body, json }) => {
729
- let attributes = `type="application/json" data-type="svelte-data" data-url=${escape_html_attr(
730
- url
731
- )}`;
732
- if (body) attributes += ` data-body="${hash(body)}"`;
733
-
734
- return `<script ${attributes}>${json}</script>`;
735
- })
736
- .join('\n\n\t');
689
+ if (inlined_style) {
690
+ head += `\n\t<style${options.dev ? ' data-svelte' : ''}>${inlined_style}</style>`;
691
+ }
692
+ // prettier-ignore
693
+ head += Array.from(css)
694
+ .map((dep) => `\n\t<link${styles.has(dep) ? ' disabled' : ''} rel="stylesheet" href="${options.prefix + dep}">`)
695
+ .join('');
696
+
697
+ if (page_config.router || page_config.hydrate) {
698
+ head += Array.from(js)
699
+ .map((dep) => `\n\t<link rel="modulepreload" href="${options.prefix + dep}">`)
700
+ .join('');
701
+ // prettier-ignore
702
+ head += `
703
+ <script type="module">
704
+ import { start } from ${s(options.prefix + options.manifest._.entry.file)};
705
+ start({
706
+ target: ${options.target ? `document.querySelector(${s(options.target)})` : 'document.body'},
707
+ paths: ${s(options.paths)},
708
+ session: ${try_serialize($session, (error) => {
709
+ throw new Error(`Failed to serialize session data: ${error.message}`);
710
+ })},
711
+ route: ${!!page_config.router},
712
+ spa: ${!ssr},
713
+ trailing_slash: ${s(options.trailing_slash)},
714
+ hydrate: ${ssr && page_config.hydrate ? `{
715
+ status: ${status},
716
+ error: ${serialize_error(error)},
717
+ nodes: [
718
+ ${(branch || [])
719
+ .map(({ node }) => `import(${s(options.prefix + node.entry)})`)
720
+ .join(',\n\t\t\t\t\t\t')}
721
+ ],
722
+ url: new URL(${s(url.href)}),
723
+ params: ${devalue(params)}
724
+ }` : 'null'}
725
+ });
726
+ </script>${options.service_worker ? `
727
+ <script>
728
+ if ('serviceWorker' in navigator) {
729
+ navigator.serviceWorker.register('${options.service_worker}');
730
+ }
731
+ </script>` : ''}`;
732
+
733
+ body += serialized_data
734
+ .map(({ url, body, json }) => {
735
+ let attributes = `type="application/json" data-type="svelte-data" data-url=${escape_html_attr(
736
+ url
737
+ )}`;
738
+ if (body) attributes += ` data-body="${hash(body)}"`;
739
+
740
+ return `<script ${attributes}>${json}</script>`;
741
+ })
742
+ .join('\n\n\t');
743
+ }
737
744
  }
738
745
 
739
746
  /** @type {import('types/helper').ResponseHeaders} */
@@ -908,7 +915,6 @@ function is_root_relative(path) {
908
915
  * node: import('types/internal').SSRNode;
909
916
  * $session: any;
910
917
  * stuff: Record<string, any>;
911
- * prerender_enabled: boolean;
912
918
  * is_error: boolean;
913
919
  * status?: number;
914
920
  * error?: Error;
@@ -925,7 +931,6 @@ async function load_node({
925
931
  node,
926
932
  $session,
927
933
  stuff,
928
- prerender_enabled,
929
934
  is_error,
930
935
  status,
931
936
  error
@@ -950,19 +955,10 @@ async function load_node({
950
955
 
951
956
  let loaded;
952
957
 
953
- const url_proxy = new Proxy(url, {
954
- get: (target, prop, receiver) => {
955
- if (prerender_enabled && (prop === 'search' || prop === 'searchParams')) {
956
- throw new Error('Cannot access query on a page with prerendering enabled');
957
- }
958
- return Reflect.get(target, prop, receiver);
959
- }
960
- });
961
-
962
958
  if (module.load) {
963
959
  /** @type {import('types/page').LoadInput | import('types/page').ErrorLoadInput} */
964
960
  const load_input = {
965
- url: url_proxy,
961
+ url: state.prerender ? create_prerendering_url_proxy(url) : url,
966
962
  params,
967
963
  get session() {
968
964
  uses_credentials = true;
@@ -1209,7 +1205,6 @@ async function load_node({
1209
1205
 
1210
1206
  /**
1211
1207
  * @typedef {import('./types.js').Loaded} Loaded
1212
- * @typedef {import('types/internal').SSRNode} SSRNode
1213
1208
  * @typedef {import('types/internal').SSRRenderOptions} SSRRenderOptions
1214
1209
  * @typedef {import('types/internal').SSRRenderState} SSRRenderState
1215
1210
  */
@@ -1252,7 +1247,6 @@ async function respond_with_error({
1252
1247
  node: default_layout,
1253
1248
  $session,
1254
1249
  stuff: {},
1255
- prerender_enabled: is_prerender_enabled(options, default_error, state),
1256
1250
  is_error: false
1257
1251
  })
1258
1252
  );
@@ -1268,7 +1262,6 @@ async function respond_with_error({
1268
1262
  node: default_error,
1269
1263
  $session,
1270
1264
  stuff: layout_loaded ? layout_loaded.stuff : {},
1271
- prerender_enabled: is_prerender_enabled(options, default_error, state),
1272
1265
  is_error: true,
1273
1266
  status,
1274
1267
  error
@@ -1277,6 +1270,7 @@ async function respond_with_error({
1277
1270
 
1278
1271
  return await render_response({
1279
1272
  options,
1273
+ state,
1280
1274
  $session,
1281
1275
  page_config: {
1282
1276
  hydrate: options.hydrate,
@@ -1303,17 +1297,6 @@ async function respond_with_error({
1303
1297
  }
1304
1298
  }
1305
1299
 
1306
- /**
1307
- * @param {SSRRenderOptions} options
1308
- * @param {SSRNode} node
1309
- * @param {SSRRenderState} state
1310
- */
1311
- function is_prerender_enabled(options, node, state) {
1312
- return (
1313
- options.prerender && (!!node.module.prerender || (!!state.prerender && state.prerender.all))
1314
- );
1315
- }
1316
-
1317
1300
  /**
1318
1301
  * @typedef {import('./types.js').Loaded} Loaded
1319
1302
  * @typedef {import('types/hooks').ServerResponse} ServerResponse
@@ -1416,7 +1399,6 @@ async function respond$1(opts) {
1416
1399
  url: request.url,
1417
1400
  node,
1418
1401
  stuff,
1419
- prerender_enabled: is_prerender_enabled(options, node, state),
1420
1402
  is_error: false
1421
1403
  });
1422
1404
 
@@ -1471,7 +1453,6 @@ async function respond$1(opts) {
1471
1453
  url: request.url,
1472
1454
  node: error_node,
1473
1455
  stuff: node_loaded.stuff,
1474
- prerender_enabled: is_prerender_enabled(options, error_node, state),
1475
1456
  is_error: true,
1476
1457
  status,
1477
1458
  error
@@ -1641,8 +1622,9 @@ function read_only_form_data() {
1641
1622
  * @param {string} value
1642
1623
  */
1643
1624
  append(key, value) {
1644
- if (map.has(key)) {
1645
- (map.get(key) || []).push(value);
1625
+ const existing_values = map.get(key);
1626
+ if (existing_values) {
1627
+ existing_values.push(value);
1646
1628
  } else {
1647
1629
  map.set(key, [value]);
1648
1630
  }
@@ -1664,12 +1646,15 @@ class ReadOnlyFormData {
1664
1646
  /** @param {string} key */
1665
1647
  get(key) {
1666
1648
  const value = this.#map.get(key);
1667
- return value && value[0];
1649
+ if (!value) {
1650
+ return null;
1651
+ }
1652
+ return value[0];
1668
1653
  }
1669
1654
 
1670
1655
  /** @param {string} key */
1671
1656
  getAll(key) {
1672
- return this.#map.get(key);
1657
+ return this.#map.get(key) || [];
1673
1658
  }
1674
1659
 
1675
1660
  /** @param {string} key */
@@ -1901,6 +1886,7 @@ async function respond(incoming, options, state = {}) {
1901
1886
  url: request.url,
1902
1887
  params: request.params,
1903
1888
  options,
1889
+ state,
1904
1890
  $session: await options.hooks.getSession(request),
1905
1891
  page_config: { router: true, hydrate: true },
1906
1892
  stuff: {},
@@ -1935,9 +1921,25 @@ async function respond(incoming, options, state = {}) {
1935
1921
  const etag = `"${hash(response.body || '')}"`;
1936
1922
 
1937
1923
  if (if_none_match_value === etag) {
1924
+ /** @type {import('types/helper').ResponseHeaders} */
1925
+ const headers = { etag };
1926
+
1927
+ // https://datatracker.ietf.org/doc/html/rfc7232#section-4.1
1928
+ for (const key of [
1929
+ 'cache-control',
1930
+ 'content-location',
1931
+ 'date',
1932
+ 'expires',
1933
+ 'vary'
1934
+ ]) {
1935
+ if (key in response.headers) {
1936
+ headers[key] = /** @type {string} */ (response.headers[key]);
1937
+ }
1938
+ }
1939
+
1938
1940
  return {
1939
1941
  status: 304,
1940
- headers: {}
1942
+ headers
1941
1943
  };
1942
1944
  }
1943
1945
 
@@ -19,6 +19,8 @@ const goto = import.meta.env.SSR ? guard('goto') : goto_;
19
19
  const invalidate = import.meta.env.SSR ? guard('invalidate') : invalidate_;
20
20
  const prefetch = import.meta.env.SSR ? guard('prefetch') : prefetch_;
21
21
  const prefetchRoutes = import.meta.env.SSR ? guard('prefetchRoutes') : prefetchRoutes_;
22
+ const beforeNavigate = import.meta.env.SSR ? () => {} : beforeNavigate_;
23
+ const afterNavigate = import.meta.env.SSR ? () => {} : afterNavigate_;
22
24
 
23
25
  /**
24
26
  * @type {import('$app/navigation').goto}
@@ -62,4 +64,18 @@ async function prefetchRoutes_(pathnames) {
62
64
  await Promise.all(promises);
63
65
  }
64
66
 
65
- export { disableScrollHandling, goto, invalidate, prefetch, prefetchRoutes };
67
+ /**
68
+ * @type {import('$app/navigation').beforeNavigate}
69
+ */
70
+ function beforeNavigate_(fn) {
71
+ if (router) router.before_navigate(fn);
72
+ }
73
+
74
+ /**
75
+ * @type {import('$app/navigation').afterNavigate}
76
+ */
77
+ function afterNavigate_(fn) {
78
+ if (router) router.after_navigate(fn);
79
+ }
80
+
81
+ export { afterNavigate, beforeNavigate, disableScrollHandling, goto, invalidate, prefetch, prefetchRoutes };