@sveltejs/kit 1.0.0-next.260 → 1.0.0-next.264

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.
@@ -189,8 +189,11 @@ class Router {
189
189
  // Ignore if <a> has a target
190
190
  if (a instanceof SVGAElement ? a.target.baseVal : a.target) return;
191
191
 
192
- // Check if new url only differs by hash
193
- if (url.href.split('#')[0] === location.href.split('#')[0]) {
192
+ // Check if new url only differs by hash and use the browser default behavior in that case
193
+ // This will ensure the `hashchange` event is fired
194
+ // Removing the hash does a full page navigation in the browser, so make sure a hash is present
195
+ const [base, hash] = url.href.split('#');
196
+ if (hash !== undefined && base === location.href.split('#')[0]) {
194
197
  // Call `pushState` to add url to history so going back works.
195
198
  // Also make a delay, otherwise the browser default behaviour would not kick in
196
199
  setTimeout(() => history.pushState({}, '', url.href));
@@ -1266,11 +1269,14 @@ class Renderer {
1266
1269
  let props = {};
1267
1270
 
1268
1271
  if (has_shadow && i === a.length - 1) {
1269
- const res = await fetch(`${url.pathname}/__data.json`, {
1270
- headers: {
1271
- 'x-sveltekit-noredirect': 'true'
1272
+ const res = await fetch(
1273
+ `${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}__data.json`,
1274
+ {
1275
+ headers: {
1276
+ 'x-sveltekit-noredirect': 'true'
1277
+ }
1272
1278
  }
1273
- });
1279
+ );
1274
1280
 
1275
1281
  if (res.ok) {
1276
1282
  const redirect = res.headers.get('x-sveltekit-location');
@@ -1458,10 +1464,6 @@ class Renderer {
1458
1464
  * }} opts
1459
1465
  */
1460
1466
  async function start({ paths, target, session, route, spa, trailing_slash, hydrate }) {
1461
- if (import.meta.env.DEV && !target) {
1462
- throw new Error('Missing target element. See https://kit.svelte.dev/docs#configuration-target');
1463
- }
1464
-
1465
1467
  const renderer = new Renderer({
1466
1468
  Root,
1467
1469
  fallback,
@@ -7,12 +7,12 @@ function to_headers(object) {
7
7
  const value = object[key];
8
8
  if (!value) continue;
9
9
 
10
- if (typeof value === 'string') {
11
- headers.set(key, value);
12
- } else {
10
+ if (Array.isArray(value)) {
13
11
  value.forEach((value) => {
14
- headers.append(key, value);
12
+ headers.append(key, /** @type {string} */ (value));
15
13
  });
14
+ } else {
15
+ headers.set(key, /** @type {string} */ (value));
16
16
  }
17
17
  }
18
18
  }
@@ -38,6 +38,16 @@ function hash(value) {
38
38
  }
39
39
 
40
40
  /** @param {Record<string, any>} obj */
41
+ function lowercase_keys(obj) {
42
+ /** @type {Record<string, any>} */
43
+ const clone = {};
44
+
45
+ for (const key in obj) {
46
+ clone[key.toLowerCase()] = obj[key];
47
+ }
48
+
49
+ return clone;
50
+ }
41
51
 
42
52
  /** @param {Record<string, string>} params */
43
53
  function decode_params(params) {
@@ -474,7 +484,16 @@ function coalesce_to_error(err) {
474
484
  }
475
485
 
476
486
  /** @type {Record<string, string>} */
477
- const escape_json_string_in_html_dict = {
487
+ const escape_json_in_html_dict = {
488
+ '&': '\\u0026',
489
+ '>': '\\u003e',
490
+ '<': '\\u003c',
491
+ '\u2028': '\\u2028',
492
+ '\u2029': '\\u2029'
493
+ };
494
+
495
+ /** @type {Record<string, string>} */
496
+ const escape_json_value_in_html_dict = {
478
497
  '"': '\\"',
479
498
  '<': '\\u003C',
480
499
  '>': '\\u003E',
@@ -490,36 +509,30 @@ const escape_json_string_in_html_dict = {
490
509
  '\u2029': '\\u2029'
491
510
  };
492
511
 
493
- /** @param {string} str */
494
- function escape_json_string_in_html(str) {
495
- return escape(
496
- str,
497
- escape_json_string_in_html_dict,
498
- (code) => `\\u${code.toString(16).toUpperCase()}`
499
- );
512
+ /**
513
+ * Escape a stringified JSON object that's going to be embedded in a `<script>` tag
514
+ * @param {string} str
515
+ */
516
+ function escape_json_in_html(str) {
517
+ // adapted from https://github.com/vercel/next.js/blob/694407450638b037673c6d714bfe4126aeded740/packages/next/server/htmlescape.ts
518
+ // based on https://github.com/zertosh/htmlescape
519
+ // License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE
520
+ return str.replace(/[&><\u2028\u2029]/g, (match) => escape_json_in_html_dict[match]);
500
521
  }
501
522
 
502
- /** @type {Record<string, string>} */
503
- const escape_html_attr_dict = {
504
- '<': '&lt;',
505
- '>': '&gt;',
506
- '"': '&quot;'
507
- };
508
-
509
523
  /**
510
- * use for escaping string values to be used html attributes on the page
511
- * e.g.
512
- * <script data-url="here">
513
- *
524
+ * Escape a string JSON value to be embedded into a `<script>` tag
514
525
  * @param {string} str
515
- * @returns string escaped string
516
526
  */
517
- function escape_html_attr(str) {
518
- return '"' + escape(str, escape_html_attr_dict, (code) => `&#${code};`) + '"';
527
+ function escape_json_value_in_html(str) {
528
+ return escape(
529
+ str,
530
+ escape_json_value_in_html_dict,
531
+ (code) => `\\u${code.toString(16).toUpperCase()}`
532
+ );
519
533
  }
520
534
 
521
535
  /**
522
- *
523
536
  * @param str {string} string to escape
524
537
  * @param dict {Record<string, string>} dictionary of character replacements
525
538
  * @param unicode_encoder {function(number): string} encoder to use for high unicode characters
@@ -552,6 +565,25 @@ function escape(str, dict, unicode_encoder) {
552
565
  return result;
553
566
  }
554
567
 
568
+ /** @type {Record<string, string>} */
569
+ const escape_html_attr_dict = {
570
+ '<': '&lt;',
571
+ '>': '&gt;',
572
+ '"': '&quot;'
573
+ };
574
+
575
+ /**
576
+ * use for escaping string values to be used html attributes on the page
577
+ * e.g.
578
+ * <script data-url="here">
579
+ *
580
+ * @param {string} str
581
+ * @returns string escaped string
582
+ */
583
+ function escape_html_attr(str) {
584
+ return '"' + escape(str, escape_html_attr_dict, (code) => `&#${code};`) + '"';
585
+ }
586
+
555
587
  const s = JSON.stringify;
556
588
 
557
589
  /** @param {URL} url */
@@ -1269,7 +1301,7 @@ async function render_response({
1269
1301
 
1270
1302
  if (shadow_props) {
1271
1303
  // prettier-ignore
1272
- body += `<script type="application/json" data-type="svelte-props">${s(shadow_props)}</script>`;
1304
+ body += `<script type="application/json" data-type="svelte-props">${escape_json_in_html(s(shadow_props))}</script>`;
1273
1305
  }
1274
1306
  }
1275
1307
 
@@ -1714,7 +1746,7 @@ async function load_node({
1714
1746
  fetched.push({
1715
1747
  url: requested,
1716
1748
  body: /** @type {string} */ (opts.body),
1717
- json: `{"status":${response.status},"statusText":${s(response.statusText)},"headers":${s(headers)},"body":"${escape_json_string_in_html(body)}"}`
1749
+ json: `{"status":${response.status},"statusText":${s(response.statusText)},"headers":${s(headers)},"body":"${escape_json_value_in_html(body)}"}`
1718
1750
  });
1719
1751
  }
1720
1752
 
@@ -1854,13 +1886,8 @@ async function load_shadow_data(route, event, prerender) {
1854
1886
 
1855
1887
  if (result.fallthrough) return result;
1856
1888
 
1857
- const { status = 200, headers = {}, body = {} } = result;
1858
-
1859
- validate_shadow_output(headers, body);
1860
-
1861
- if (headers['set-cookie']) {
1862
- /** @type {string[]} */ (data.cookies).push(...headers['set-cookie']);
1863
- }
1889
+ const { status, headers, body } = validate_shadow_output(result);
1890
+ add_cookies(/** @type {string[]} */ (data.cookies), headers);
1864
1891
 
1865
1892
  // Redirects are respected...
1866
1893
  if (status >= 300 && status < 400) {
@@ -1884,13 +1911,8 @@ async function load_shadow_data(route, event, prerender) {
1884
1911
 
1885
1912
  if (result.fallthrough) return result;
1886
1913
 
1887
- const { status = 200, headers = {}, body = {} } = result;
1888
-
1889
- validate_shadow_output(headers, body);
1890
-
1891
- if (headers['set-cookie']) {
1892
- /** @type {string[]} */ (data.cookies).push(...headers['set-cookie']);
1893
- }
1914
+ const { status, headers, body } = validate_shadow_output(result);
1915
+ add_cookies(/** @type {string[]} */ (data.cookies), headers);
1894
1916
 
1895
1917
  if (status >= 400) {
1896
1918
  return {
@@ -1921,19 +1943,42 @@ async function load_shadow_data(route, event, prerender) {
1921
1943
  }
1922
1944
 
1923
1945
  /**
1924
- * @param {Headers | Partial<import('types/helper').ResponseHeaders>} headers
1925
- * @param {import('types/helper').JSONValue} body
1946
+ * @param {string[]} target
1947
+ * @param {Partial<import('types/helper').ResponseHeaders>} headers
1926
1948
  */
1927
- function validate_shadow_output(headers, body) {
1928
- if (headers instanceof Headers && headers.has('set-cookie')) {
1929
- throw new Error(
1930
- 'Shadow endpoint request handler cannot use Headers interface with Set-Cookie headers'
1931
- );
1949
+ function add_cookies(target, headers) {
1950
+ const cookies = headers['set-cookie'];
1951
+ if (cookies) {
1952
+ if (Array.isArray(cookies)) {
1953
+ target.push(...cookies);
1954
+ } else {
1955
+ target.push(/** @type {string} */ (cookies));
1956
+ }
1957
+ }
1958
+ }
1959
+
1960
+ /**
1961
+ * @param {import('types/endpoint').ShadowEndpointOutput} result
1962
+ */
1963
+ function validate_shadow_output(result) {
1964
+ const { status = 200, body = {} } = result;
1965
+ let headers = result.headers || {};
1966
+
1967
+ if (headers instanceof Headers) {
1968
+ if (headers.has('set-cookie')) {
1969
+ throw new Error(
1970
+ 'Shadow endpoint request handler cannot use Headers interface with Set-Cookie headers'
1971
+ );
1972
+ }
1973
+ } else {
1974
+ headers = lowercase_keys(/** @type {Record<string, string>} */ (headers));
1932
1975
  }
1933
1976
 
1934
1977
  if (!is_pojo(body)) {
1935
1978
  throw new Error('Body returned from shadow endpoint request handler must be a plain object');
1936
1979
  }
1980
+
1981
+ return { status, headers, body };
1937
1982
  }
1938
1983
 
1939
1984
  /**
@@ -2266,7 +2311,7 @@ function get_page_config(leaf, options) {
2266
2311
  // TODO remove for 1.0
2267
2312
  if ('ssr' in leaf) {
2268
2313
  throw new Error(
2269
- '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs#hooks-handle'
2314
+ '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle'
2270
2315
  );
2271
2316
  }
2272
2317
 
@@ -2437,7 +2482,7 @@ async function respond(request, options, state = {}) {
2437
2482
  });
2438
2483
  } else {
2439
2484
  const verb = allowed.length === 0 ? 'enabled' : 'allowed';
2440
- const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs#configuration-methodoverride`;
2485
+ const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs/configuration#methodoverride`;
2441
2486
 
2442
2487
  return new Response(body, {
2443
2488
  status: 400
@@ -211,7 +211,7 @@ async function create_plugin(config, cwd) {
211
211
  if (/** @type {any} */ (hooks).getContext) {
212
212
  // TODO remove this for 1.0
213
213
  throw new Error(
214
- 'The getContext hook has been removed. See https://kit.svelte.dev/docs#hooks'
214
+ 'The getContext hook has been removed. See https://kit.svelte.dev/docs/hooks'
215
215
  );
216
216
  }
217
217
 
@@ -333,27 +333,7 @@ function crawl(html) {
333
333
 
334
334
  /** @type {Record<string, string>} */
335
335
 
336
- /** @type {Record<string, string>} */
337
- const escape_html_attr_dict = {
338
- '<': '&lt;',
339
- '>': '&gt;',
340
- '"': '&quot;'
341
- };
342
-
343
- /**
344
- * use for escaping string values to be used html attributes on the page
345
- * e.g.
346
- * <script data-url="here">
347
- *
348
- * @param {string} str
349
- * @returns string escaped string
350
- */
351
- function escape_html_attr(str) {
352
- return '"' + escape(str, escape_html_attr_dict, (code) => `&#${code};`) + '"';
353
- }
354
-
355
336
  /**
356
- *
357
337
  * @param str {string} string to escape
358
338
  * @param dict {Record<string, string>} dictionary of character replacements
359
339
  * @param unicode_encoder {function(number): string} encoder to use for high unicode characters
@@ -386,6 +366,25 @@ function escape(str, dict, unicode_encoder) {
386
366
  return result;
387
367
  }
388
368
 
369
+ /** @type {Record<string, string>} */
370
+ const escape_html_attr_dict = {
371
+ '<': '&lt;',
372
+ '>': '&gt;',
373
+ '"': '&quot;'
374
+ };
375
+
376
+ /**
377
+ * use for escaping string values to be used html attributes on the page
378
+ * e.g.
379
+ * <script data-url="here">
380
+ *
381
+ * @param {string} str
382
+ * @returns string escaped string
383
+ */
384
+ function escape_html_attr(str) {
385
+ return '"' + escape(str, escape_html_attr_dict, (code) => `&#${code};`) + '"';
386
+ }
387
+
389
388
  /**
390
389
  * @typedef {import('types/config').PrerenderErrorHandler} PrerenderErrorHandler
391
390
  * @typedef {import('types/config').PrerenderOnErrorValue} OnError
@@ -15296,14 +15296,16 @@ async function make_package(config, cwd = process.cwd()) {
15296
15296
  const svelte_ext = config.extensions.find((ext) => file.endsWith(ext)); // unlike `ext`, could be e.g. `.svelte.md`
15297
15297
 
15298
15298
  if (!config.kit.package.files(normalized)) {
15299
- const dts_file = (svelte_ext ? file : file.slice(0, -ext.length)) + '.d.ts';
15300
- const dts_path = path.join(package_dir, dts_file);
15301
- if (fs.existsSync(dts_path)) {
15302
- fs.unlinkSync(dts_path);
15303
-
15304
- const dir = path.dirname(dts_path);
15305
- if (fs.readdirSync(dir).length === 0) {
15306
- fs.rmdirSync(dir);
15299
+ const base = svelte_ext ? file : file.slice(0, -ext.length);
15300
+ for (const e of ['.d.ts', '.d.mts', '.d.cts']) {
15301
+ const dts_path = path.join(package_dir, base + e);
15302
+ if (fs.existsSync(dts_path)) {
15303
+ fs.unlinkSync(dts_path);
15304
+
15305
+ const dir = path.dirname(dts_path);
15306
+ if (fs.readdirSync(dir).length === 0) {
15307
+ fs.rmdirSync(dir);
15308
+ }
15307
15309
  }
15308
15310
  }
15309
15311
  continue;
@@ -15573,7 +15575,7 @@ async function try_load_svelte2tsx() {
15573
15575
  return await import('svelte2tsx');
15574
15576
  } catch (e) {
15575
15577
  throw new Error(
15576
- 'You need svelte2tsx and typescript if you want to generate type definitions. Install it through your package manager, or disable generation which is highly discouraged. See https://kit.svelte.dev/docs#packaging'
15578
+ 'You need svelte2tsx and typescript if you want to generate type definitions. Install it through your package manager, or disable generation which is highly discouraged. See https://kit.svelte.dev/docs/packaging'
15577
15579
  );
15578
15580
  }
15579
15581
  }
package/dist/cli.js CHANGED
@@ -466,7 +466,7 @@ const options = object(
466
466
  message += ', rather than the name of an adapter';
467
467
  }
468
468
 
469
- throw new Error(`${message}. See https://kit.svelte.dev/docs#adapters`);
469
+ throw new Error(`${message}. See https://kit.svelte.dev/docs/adapters`);
470
470
  }
471
471
 
472
472
  return input;
@@ -480,7 +480,7 @@ const options = object(
480
480
  if (input) {
481
481
  if (input.startsWith('/') || input.endsWith('/')) {
482
482
  throw new Error(
483
- "config.kit.appDir cannot start or end with '/'. See https://kit.svelte.dev/docs#configuration"
483
+ "config.kit.appDir cannot start or end with '/'. See https://kit.svelte.dev/docs/configuration"
484
484
  );
485
485
  }
486
486
  } else {
@@ -589,7 +589,7 @@ const options = object(
589
589
 
590
590
  if (input !== '' && (input.endsWith('/') || !input.startsWith('/'))) {
591
591
  throw new Error(
592
- `${keypath} option must be a root-relative path that starts but doesn't end with '/'. See https://kit.svelte.dev/docs#configuration-paths`
592
+ `${keypath} option must be a root-relative path that starts but doesn't end with '/'. See https://kit.svelte.dev/docs/configuration#paths`
593
593
  );
594
594
  }
595
595
 
@@ -601,13 +601,13 @@ const options = object(
601
601
  if (input) {
602
602
  if (!/^[a-z]+:\/\//.test(input)) {
603
603
  throw new Error(
604
- `${keypath} option must be an absolute path, if specified. See https://kit.svelte.dev/docs#configuration-paths`
604
+ `${keypath} option must be an absolute path, if specified. See https://kit.svelte.dev/docs/configuration#paths`
605
605
  );
606
606
  }
607
607
 
608
608
  if (input.endsWith('/')) {
609
609
  throw new Error(
610
- `${keypath} option must not end with '/'. See https://kit.svelte.dev/docs#configuration-paths`
610
+ `${keypath} option must not end with '/'. See https://kit.svelte.dev/docs/configuration#paths`
611
611
  );
612
612
  }
613
613
  }
@@ -681,7 +681,7 @@ const options = object(
681
681
  // TODO remove this for 1.0
682
682
  ssr: error(
683
683
  (keypath) =>
684
- `${keypath} has been removed — use the handle hook instead: https://kit.svelte.dev/docs#hooks-handle'`
684
+ `${keypath} has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle'`
685
685
  ),
686
686
 
687
687
  // TODO remove this for 1.0
@@ -904,7 +904,7 @@ async function load_config({ cwd = process.cwd() } = {}) {
904
904
 
905
905
  if (!fs__default.existsSync(config_file)) {
906
906
  throw new Error(
907
- 'You need to create a svelte.config.js file. See https://kit.svelte.dev/docs#configuration'
907
+ 'You need to create a svelte.config.js file. See https://kit.svelte.dev/docs/configuration'
908
908
  );
909
909
  }
910
910
 
@@ -929,7 +929,7 @@ async function load_config({ cwd = process.cwd() } = {}) {
929
929
  function validate_config(config) {
930
930
  if (typeof config !== 'object') {
931
931
  throw new Error(
932
- 'svelte.config.js must have a configuration object as its default export. See https://kit.svelte.dev/docs#configuration'
932
+ 'svelte.config.js must have a configuration object as its default export. See https://kit.svelte.dev/docs/configuration'
933
933
  );
934
934
  }
935
935
 
@@ -995,7 +995,7 @@ async function launch(port, https) {
995
995
  exec(`${cmd} ${https ? 'https' : 'http'}://localhost:${port}`);
996
996
  }
997
997
 
998
- const prog = sade('svelte-kit').version('1.0.0-next.260');
998
+ const prog = sade('svelte-kit').version('1.0.0-next.264');
999
999
 
1000
1000
  prog
1001
1001
  .command('dev')
@@ -1066,7 +1066,7 @@ prog
1066
1066
 
1067
1067
  // prettier-ignore
1068
1068
  console.log(
1069
- `See ${$.bold().cyan('https://kit.svelte.dev/docs#adapters')} to learn how to configure your app to run on the platform of your choosing`
1069
+ `See ${$.bold().cyan('https://kit.svelte.dev/docs/adapters')} to learn how to configure your app to run on the platform of your choosing`
1070
1070
  );
1071
1071
  } catch (error) {
1072
1072
  handle_error(error);
@@ -1153,7 +1153,7 @@ async function check_port(port) {
1153
1153
  function welcome({ port, host, https, open, loose, allow, cwd }) {
1154
1154
  if (open) launch(port, https);
1155
1155
 
1156
- console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.260'}\n`));
1156
+ console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.264'}\n`));
1157
1157
 
1158
1158
  const protocol = https ? 'https:' : 'http:';
1159
1159
  const exposed = typeof host !== 'undefined' && host !== 'localhost' && host !== '127.0.0.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.260",
3
+ "version": "1.0.0-next.264",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -12,7 +12,7 @@
12
12
  "dependencies": {
13
13
  "@sveltejs/vite-plugin-svelte": "^1.0.0-next.32",
14
14
  "sade": "^1.7.4",
15
- "vite": "^2.7.2"
15
+ "vite": "^2.8.0"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@playwright/test": "^1.17.1",
package/types/helper.d.ts CHANGED
@@ -4,7 +4,7 @@ export type JSONObject = { [key: string]: JSONValue };
4
4
  export type JSONValue = string | number | boolean | null | ToJSON | JSONValue[] | JSONObject;
5
5
 
6
6
  /** `string[]` is only for set-cookie, everything else must be type of `string` */
7
- export type ResponseHeaders = Record<string, string | string[]>;
7
+ export type ResponseHeaders = Record<string, string | number | string[]>;
8
8
 
9
9
  // <-- Utility Types -->
10
10
  type Only<T, U> = { [P in keyof T]: T[P] } & { [P in Exclude<keyof U, keyof T>]?: never };