@sveltejs/kit 1.0.0-next.265 → 1.0.0-next.269
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/client/start.js +17 -8
- package/assets/server/index.js +74 -81
- package/dist/chunks/index5.js +158 -129
- package/dist/cli.js +5 -4
- package/package.json +1 -1
- package/types/config.d.ts +27 -3
- package/types/index.d.ts +8 -1
package/assets/client/start.js
CHANGED
|
@@ -6,6 +6,22 @@ import { writable } from 'svelte/store';
|
|
|
6
6
|
import { base, set_paths } from '../paths.js';
|
|
7
7
|
import { init } from './singletons.js';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} path
|
|
11
|
+
* @param {'always' | 'never' | 'ignore'} trailing_slash
|
|
12
|
+
*/
|
|
13
|
+
function normalize_path(path, trailing_slash) {
|
|
14
|
+
if (path === '/' || trailing_slash === 'ignore') return path;
|
|
15
|
+
|
|
16
|
+
if (trailing_slash === 'never') {
|
|
17
|
+
return path.endsWith('/') ? path.slice(0, -1) : path;
|
|
18
|
+
} else if (trailing_slash === 'always' && /\/[^./]+$/.test(path)) {
|
|
19
|
+
return path + '/';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return path;
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
function scroll_state() {
|
|
10
26
|
return {
|
|
11
27
|
x: pageXOffset,
|
|
@@ -396,14 +412,7 @@ class Router {
|
|
|
396
412
|
}
|
|
397
413
|
this.navigating++;
|
|
398
414
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if (this.trailing_slash === 'never') {
|
|
402
|
-
if (pathname !== '/' && pathname.endsWith('/')) pathname = pathname.slice(0, -1);
|
|
403
|
-
} else if (this.trailing_slash === 'always') {
|
|
404
|
-
const is_file = /** @type {string} */ (url.pathname.split('/').pop()).includes('.');
|
|
405
|
-
if (!is_file && !pathname.endsWith('/')) pathname += '/';
|
|
406
|
-
}
|
|
415
|
+
const pathname = normalize_path(url.pathname, this.trailing_slash);
|
|
407
416
|
|
|
408
417
|
info.url = new URL(url.origin + pathname + url.search + url.hash);
|
|
409
418
|
|
package/assets/server/index.js
CHANGED
|
@@ -147,7 +147,9 @@ async function render_endpoint(event, mod) {
|
|
|
147
147
|
|
|
148
148
|
const { status = 200, body = {} } = response;
|
|
149
149
|
const headers =
|
|
150
|
-
response.headers instanceof Headers
|
|
150
|
+
response.headers instanceof Headers
|
|
151
|
+
? new Headers(response.headers)
|
|
152
|
+
: to_headers(response.headers);
|
|
151
153
|
|
|
152
154
|
const type = headers.get('content-type');
|
|
153
155
|
|
|
@@ -483,52 +485,29 @@ function coalesce_to_error(err) {
|
|
|
483
485
|
: new Error(JSON.stringify(err));
|
|
484
486
|
}
|
|
485
487
|
|
|
488
|
+
// dict from https://github.com/yahoo/serialize-javascript/blob/183c18a776e4635a379fdc620f81771f219832bb/index.js#L25
|
|
486
489
|
/** @type {Record<string, string>} */
|
|
487
490
|
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 = {
|
|
497
|
-
'"': '\\"',
|
|
498
491
|
'<': '\\u003C',
|
|
499
492
|
'>': '\\u003E',
|
|
500
493
|
'/': '\\u002F',
|
|
501
|
-
'\\': '\\\\',
|
|
502
|
-
'\b': '\\b',
|
|
503
|
-
'\f': '\\f',
|
|
504
|
-
'\n': '\\n',
|
|
505
|
-
'\r': '\\r',
|
|
506
|
-
'\t': '\\t',
|
|
507
|
-
'\0': '\\0',
|
|
508
494
|
'\u2028': '\\u2028',
|
|
509
495
|
'\u2029': '\\u2029'
|
|
510
496
|
};
|
|
511
497
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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]);
|
|
521
|
-
}
|
|
498
|
+
const escape_json_in_html_regex = new RegExp(
|
|
499
|
+
`[${Object.keys(escape_json_in_html_dict).join('')}]`,
|
|
500
|
+
'g'
|
|
501
|
+
);
|
|
522
502
|
|
|
523
503
|
/**
|
|
524
|
-
* Escape a
|
|
525
|
-
* @param {
|
|
504
|
+
* Escape a JSONValue that's going to be embedded in a `<script>` tag
|
|
505
|
+
* @param {import("@sveltejs/kit/types/helper").JSONValue} val
|
|
526
506
|
*/
|
|
527
|
-
function
|
|
528
|
-
return
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
(code) => `\\u${code.toString(16).toUpperCase()}`
|
|
507
|
+
function escape_json_in_html(val) {
|
|
508
|
+
return JSON.stringify(val).replace(
|
|
509
|
+
escape_json_in_html_regex,
|
|
510
|
+
(match) => escape_json_in_html_dict[match]
|
|
532
511
|
);
|
|
533
512
|
}
|
|
534
513
|
|
|
@@ -1301,7 +1280,7 @@ async function render_response({
|
|
|
1301
1280
|
|
|
1302
1281
|
if (shadow_props) {
|
|
1303
1282
|
// prettier-ignore
|
|
1304
|
-
body += `<script type="application/json" data-type="svelte-props">${escape_json_in_html(
|
|
1283
|
+
body += `<script type="application/json" data-type="svelte-props">${escape_json_in_html(shadow_props)}</script>`;
|
|
1305
1284
|
}
|
|
1306
1285
|
}
|
|
1307
1286
|
|
|
@@ -1496,6 +1475,22 @@ function is_root_relative(path) {
|
|
|
1496
1475
|
return path[0] === '/' && path[1] !== '/';
|
|
1497
1476
|
}
|
|
1498
1477
|
|
|
1478
|
+
/**
|
|
1479
|
+
* @param {string} path
|
|
1480
|
+
* @param {'always' | 'never' | 'ignore'} trailing_slash
|
|
1481
|
+
*/
|
|
1482
|
+
function normalize_path(path, trailing_slash) {
|
|
1483
|
+
if (path === '/' || trailing_slash === 'ignore') return path;
|
|
1484
|
+
|
|
1485
|
+
if (trailing_slash === 'never') {
|
|
1486
|
+
return path.endsWith('/') ? path.slice(0, -1) : path;
|
|
1487
|
+
} else if (trailing_slash === 'always' && /\/[^./]+$/.test(path)) {
|
|
1488
|
+
return path + '/';
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
return path;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1499
1494
|
/**
|
|
1500
1495
|
* @param {{
|
|
1501
1496
|
* event: import('types/hooks').RequestEvent;
|
|
@@ -1555,6 +1550,7 @@ async function load_node({
|
|
|
1555
1550
|
? await load_shadow_data(
|
|
1556
1551
|
/** @type {import('types/internal').SSRPage} */ (route),
|
|
1557
1552
|
event,
|
|
1553
|
+
options,
|
|
1558
1554
|
!!state.prerender
|
|
1559
1555
|
)
|
|
1560
1556
|
: {};
|
|
@@ -1698,9 +1694,7 @@ async function load_node({
|
|
|
1698
1694
|
} else {
|
|
1699
1695
|
// external
|
|
1700
1696
|
if (resolved.startsWith('//')) {
|
|
1701
|
-
|
|
1702
|
-
`Cannot request protocol-relative URL (${requested}) in server-side fetch`
|
|
1703
|
-
);
|
|
1697
|
+
requested = event.url.protocol + requested;
|
|
1704
1698
|
}
|
|
1705
1699
|
|
|
1706
1700
|
// external fetch
|
|
@@ -1742,11 +1736,21 @@ async function load_node({
|
|
|
1742
1736
|
}
|
|
1743
1737
|
|
|
1744
1738
|
if (!opts.body || typeof opts.body === 'string') {
|
|
1739
|
+
// the json constructed below is later added to the dom in a script tag
|
|
1740
|
+
// make sure the used values are safe
|
|
1741
|
+
const status_number = Number(response.status);
|
|
1742
|
+
if (isNaN(status_number)) {
|
|
1743
|
+
throw new Error(
|
|
1744
|
+
`response.status is not a number. value: "${
|
|
1745
|
+
response.status
|
|
1746
|
+
}" type: ${typeof response.status}`
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1745
1749
|
// prettier-ignore
|
|
1746
1750
|
fetched.push({
|
|
1747
1751
|
url: requested,
|
|
1748
1752
|
body: /** @type {string} */ (opts.body),
|
|
1749
|
-
json: `{"status":${
|
|
1753
|
+
json: `{"status":${status_number},"statusText":${s(response.statusText)},"headers":${s(headers)},"body":${escape_json_in_html(body)}}`
|
|
1750
1754
|
});
|
|
1751
1755
|
}
|
|
1752
1756
|
|
|
@@ -1851,10 +1855,11 @@ async function load_node({
|
|
|
1851
1855
|
*
|
|
1852
1856
|
* @param {import('types/internal').SSRPage} route
|
|
1853
1857
|
* @param {import('types/hooks').RequestEvent} event
|
|
1858
|
+
* @param {import('types/internal').SSROptions} options
|
|
1854
1859
|
* @param {boolean} prerender
|
|
1855
1860
|
* @returns {Promise<import('types/endpoint').ShadowData>}
|
|
1856
1861
|
*/
|
|
1857
|
-
async function load_shadow_data(route, event, prerender) {
|
|
1862
|
+
async function load_shadow_data(route, event, options, prerender) {
|
|
1858
1863
|
if (!route.shadow) return {};
|
|
1859
1864
|
|
|
1860
1865
|
try {
|
|
@@ -1887,22 +1892,21 @@ async function load_shadow_data(route, event, prerender) {
|
|
|
1887
1892
|
if (result.fallthrough) return result;
|
|
1888
1893
|
|
|
1889
1894
|
const { status, headers, body } = validate_shadow_output(result);
|
|
1895
|
+
data.status = status;
|
|
1896
|
+
|
|
1890
1897
|
add_cookies(/** @type {string[]} */ (data.cookies), headers);
|
|
1891
1898
|
|
|
1892
1899
|
// Redirects are respected...
|
|
1893
1900
|
if (status >= 300 && status < 400) {
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
)
|
|
1899
|
-
};
|
|
1901
|
+
data.redirect = /** @type {string} */ (
|
|
1902
|
+
headers instanceof Headers ? headers.get('location') : headers.location
|
|
1903
|
+
);
|
|
1904
|
+
return data;
|
|
1900
1905
|
}
|
|
1901
1906
|
|
|
1902
1907
|
// ...but 4xx and 5xx status codes _don't_ result in the error page
|
|
1903
1908
|
// rendering for non-GET requests — instead, we allow the page
|
|
1904
1909
|
// to render with any validation errors etc that were returned
|
|
1905
|
-
data.status = status;
|
|
1906
1910
|
data.body = body;
|
|
1907
1911
|
}
|
|
1908
1912
|
|
|
@@ -1913,21 +1917,18 @@ async function load_shadow_data(route, event, prerender) {
|
|
|
1913
1917
|
|
|
1914
1918
|
const { status, headers, body } = validate_shadow_output(result);
|
|
1915
1919
|
add_cookies(/** @type {string[]} */ (data.cookies), headers);
|
|
1920
|
+
data.status = status;
|
|
1916
1921
|
|
|
1917
1922
|
if (status >= 400) {
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
error: new Error('Failed to load data')
|
|
1921
|
-
};
|
|
1923
|
+
data.error = new Error('Failed to load data');
|
|
1924
|
+
return data;
|
|
1922
1925
|
}
|
|
1923
1926
|
|
|
1924
1927
|
if (status >= 300) {
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
)
|
|
1930
|
-
};
|
|
1928
|
+
data.redirect = /** @type {string} */ (
|
|
1929
|
+
headers instanceof Headers ? headers.get('location') : headers.location
|
|
1930
|
+
);
|
|
1931
|
+
return data;
|
|
1931
1932
|
}
|
|
1932
1933
|
|
|
1933
1934
|
data.body = { ...body, ...data.body };
|
|
@@ -1935,9 +1936,12 @@ async function load_shadow_data(route, event, prerender) {
|
|
|
1935
1936
|
|
|
1936
1937
|
return data;
|
|
1937
1938
|
} catch (e) {
|
|
1939
|
+
const error = coalesce_to_error(e);
|
|
1940
|
+
options.handle_error(error, event);
|
|
1941
|
+
|
|
1938
1942
|
return {
|
|
1939
1943
|
status: 500,
|
|
1940
|
-
error
|
|
1944
|
+
error
|
|
1941
1945
|
};
|
|
1942
1946
|
}
|
|
1943
1947
|
}
|
|
@@ -2446,26 +2450,15 @@ const DATA_SUFFIX = '/__data.json';
|
|
|
2446
2450
|
async function respond(request, options, state = {}) {
|
|
2447
2451
|
const url = new URL(request.url);
|
|
2448
2452
|
|
|
2449
|
-
|
|
2450
|
-
const has_trailing_slash = url.pathname.endsWith('/');
|
|
2451
|
-
|
|
2452
|
-
if (
|
|
2453
|
-
(has_trailing_slash && options.trailing_slash === 'never') ||
|
|
2454
|
-
(!has_trailing_slash &&
|
|
2455
|
-
options.trailing_slash === 'always' &&
|
|
2456
|
-
!(url.pathname.split('/').pop() || '').includes('.'))
|
|
2457
|
-
) {
|
|
2458
|
-
url.pathname = has_trailing_slash ? url.pathname.slice(0, -1) : url.pathname + '/';
|
|
2459
|
-
|
|
2460
|
-
if (url.search === '?') url.search = '';
|
|
2453
|
+
const normalized = normalize_path(url.pathname, options.trailing_slash);
|
|
2461
2454
|
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2455
|
+
if (normalized !== url.pathname) {
|
|
2456
|
+
return new Response(undefined, {
|
|
2457
|
+
status: 301,
|
|
2458
|
+
headers: {
|
|
2459
|
+
location: normalized + (url.search === '?' ? '' : url.search)
|
|
2460
|
+
}
|
|
2461
|
+
});
|
|
2469
2462
|
}
|
|
2470
2463
|
|
|
2471
2464
|
const { parameter, allowed } = options.method_override;
|
|
@@ -2594,11 +2587,11 @@ async function respond(request, options, state = {}) {
|
|
|
2594
2587
|
const location = response.headers.get('location');
|
|
2595
2588
|
|
|
2596
2589
|
if (location) {
|
|
2590
|
+
const headers = new Headers(response.headers);
|
|
2591
|
+
headers.set('x-sveltekit-location', location);
|
|
2597
2592
|
response = new Response(undefined, {
|
|
2598
2593
|
status: 204,
|
|
2599
|
-
headers
|
|
2600
|
-
'x-sveltekit-location': location
|
|
2601
|
-
}
|
|
2594
|
+
headers
|
|
2602
2595
|
});
|
|
2603
2596
|
}
|
|
2604
2597
|
}
|
package/dist/chunks/index5.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { b as SVELTE_KIT, m as mkdirp, h as rimraf, i as copy, $, j as logger } from '../cli.js';
|
|
2
2
|
import { readFileSync, writeFileSync } from 'fs';
|
|
3
3
|
import { resolve as resolve$1, join, dirname } from 'path';
|
|
4
4
|
import { pathToFileURL, URL } from 'url';
|
|
@@ -55,6 +55,22 @@ function is_root_relative(path) {
|
|
|
55
55
|
return path[0] === '/' && path[1] !== '/';
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} path
|
|
60
|
+
* @param {'always' | 'never' | 'ignore'} trailing_slash
|
|
61
|
+
*/
|
|
62
|
+
function normalize_path(path, trailing_slash) {
|
|
63
|
+
if (path === '/' || trailing_slash === 'ignore') return path;
|
|
64
|
+
|
|
65
|
+
if (trailing_slash === 'never') {
|
|
66
|
+
return path.endsWith('/') ? path.slice(0, -1) : path;
|
|
67
|
+
} else if (trailing_slash === 'always' && /\/[^./]+$/.test(path)) {
|
|
68
|
+
return path + '/';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return path;
|
|
72
|
+
}
|
|
73
|
+
|
|
58
74
|
/** @typedef {{
|
|
59
75
|
* fn: () => Promise<any>,
|
|
60
76
|
* fulfil: (value: any) => void,
|
|
@@ -331,7 +347,20 @@ function crawl(html) {
|
|
|
331
347
|
return hrefs;
|
|
332
348
|
}
|
|
333
349
|
|
|
350
|
+
// dict from https://github.com/yahoo/serialize-javascript/blob/183c18a776e4635a379fdc620f81771f219832bb/index.js#L25
|
|
334
351
|
/** @type {Record<string, string>} */
|
|
352
|
+
const escape_json_in_html_dict = {
|
|
353
|
+
'<': '\\u003C',
|
|
354
|
+
'>': '\\u003E',
|
|
355
|
+
'/': '\\u002F',
|
|
356
|
+
'\u2028': '\\u2028',
|
|
357
|
+
'\u2029': '\\u2029'
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
new RegExp(
|
|
361
|
+
`[${Object.keys(escape_json_in_html_dict).join('')}]`,
|
|
362
|
+
'g'
|
|
363
|
+
);
|
|
335
364
|
|
|
336
365
|
/**
|
|
337
366
|
* @param str {string} string to escape
|
|
@@ -425,25 +454,27 @@ const REDIRECT = 3;
|
|
|
425
454
|
* fallback?: string;
|
|
426
455
|
* all: boolean; // disregard `export const prerender = true`
|
|
427
456
|
* }} opts
|
|
428
|
-
* @returns {Promise<{ paths: string[] }>} returns a promise that resolves to an array of paths corresponding to the files that have been prerendered.
|
|
429
457
|
*/
|
|
430
458
|
async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
459
|
+
/** @type {import('types/config').Prerendered} */
|
|
460
|
+
const prerendered = {
|
|
461
|
+
pages: new Map(),
|
|
462
|
+
assets: new Map(),
|
|
463
|
+
redirects: new Map(),
|
|
464
|
+
paths: []
|
|
465
|
+
};
|
|
466
|
+
|
|
431
467
|
if (!config.kit.prerender.enabled && !fallback) {
|
|
432
|
-
return
|
|
468
|
+
return prerendered;
|
|
433
469
|
}
|
|
434
470
|
|
|
435
471
|
__fetch_polyfill();
|
|
436
472
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
const dir = resolve$1(cwd, `${SVELTE_KIT}/output`);
|
|
440
|
-
|
|
441
|
-
const seen = new Set();
|
|
442
|
-
|
|
443
|
-
const server_root = resolve$1(dir);
|
|
473
|
+
const server_root = resolve$1(cwd, `${SVELTE_KIT}/output`);
|
|
444
474
|
|
|
445
475
|
/** @type {import('types/internal').AppModule} */
|
|
446
476
|
const { App, override } = await import(pathToFileURL(`${server_root}/server/app.js`).href);
|
|
477
|
+
const { manifest } = await import(pathToFileURL(`${server_root}/server/manifest.js`).href);
|
|
447
478
|
|
|
448
479
|
override({
|
|
449
480
|
paths: config.kit.paths,
|
|
@@ -451,8 +482,6 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
451
482
|
read: (file) => readFileSync(join(config.kit.files.assets, file))
|
|
452
483
|
});
|
|
453
484
|
|
|
454
|
-
const { manifest } = await import(pathToFileURL(`${server_root}/server/manifest.js`).href);
|
|
455
|
-
|
|
456
485
|
const app = new App(manifest);
|
|
457
486
|
|
|
458
487
|
const error = normalise_error_handler(log, config.kit.prerender.onError);
|
|
@@ -463,28 +492,12 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
463
492
|
...build_data.client.assets.map((chunk) => `${config.kit.appDir}/${chunk.fileName}`)
|
|
464
493
|
]);
|
|
465
494
|
|
|
466
|
-
/** @type {string[]} */
|
|
467
|
-
const paths = [];
|
|
468
|
-
|
|
469
495
|
build_data.static.forEach((file) => {
|
|
470
496
|
if (file.endsWith('/index.html')) {
|
|
471
497
|
files.add(file.slice(0, -11));
|
|
472
498
|
}
|
|
473
499
|
});
|
|
474
500
|
|
|
475
|
-
/**
|
|
476
|
-
* @param {string} path
|
|
477
|
-
*/
|
|
478
|
-
function normalize(path) {
|
|
479
|
-
if (config.kit.trailingSlash === 'always') {
|
|
480
|
-
return path.endsWith('/') ? path : `${path}/`;
|
|
481
|
-
} else if (config.kit.trailingSlash === 'never') {
|
|
482
|
-
return !path.endsWith('/') || path === '/' ? path : path.slice(0, -1);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
return path;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
501
|
const q = queue(config.kit.prerender.concurrency);
|
|
489
502
|
|
|
490
503
|
/**
|
|
@@ -492,150 +505,168 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
492
505
|
* @param {boolean} is_html
|
|
493
506
|
*/
|
|
494
507
|
function output_filename(path, is_html) {
|
|
495
|
-
|
|
508
|
+
const file = path.slice(config.kit.paths.base.length + 1);
|
|
496
509
|
|
|
497
|
-
if (
|
|
498
|
-
return '
|
|
510
|
+
if (file === '') {
|
|
511
|
+
return 'index.html';
|
|
499
512
|
}
|
|
500
513
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
if (config.kit.trailingSlash === 'always') {
|
|
504
|
-
parts.push('index.html');
|
|
505
|
-
} else {
|
|
506
|
-
parts[parts.length - 1] += '.html';
|
|
507
|
-
}
|
|
514
|
+
if (is_html && !file.endsWith('.html')) {
|
|
515
|
+
return file + (config.kit.trailingSlash === 'always' ? 'index.html' : '.html');
|
|
508
516
|
}
|
|
509
|
-
|
|
517
|
+
|
|
518
|
+
return file;
|
|
510
519
|
}
|
|
511
520
|
|
|
521
|
+
const seen = new Set();
|
|
522
|
+
const written = new Set();
|
|
523
|
+
|
|
512
524
|
/**
|
|
513
|
-
* @param {string}
|
|
514
|
-
* @param {string
|
|
525
|
+
* @param {string | null} referrer
|
|
526
|
+
* @param {string} decoded
|
|
527
|
+
* @param {string} [encoded]
|
|
515
528
|
*/
|
|
516
|
-
function enqueue(
|
|
517
|
-
|
|
529
|
+
function enqueue(referrer, decoded, encoded) {
|
|
530
|
+
if (seen.has(decoded)) return;
|
|
531
|
+
seen.add(decoded);
|
|
518
532
|
|
|
519
|
-
|
|
520
|
-
|
|
533
|
+
const file = decoded.slice(config.kit.paths.base.length + 1);
|
|
534
|
+
if (files.has(file)) return;
|
|
521
535
|
|
|
522
|
-
return q.add(() => visit(
|
|
536
|
+
return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer));
|
|
523
537
|
}
|
|
524
538
|
|
|
525
539
|
/**
|
|
526
|
-
* @param {string}
|
|
527
|
-
* @param {string}
|
|
540
|
+
* @param {string} decoded
|
|
541
|
+
* @param {string} encoded
|
|
528
542
|
* @param {string?} referrer
|
|
529
543
|
*/
|
|
530
|
-
async function visit(
|
|
531
|
-
if (!
|
|
532
|
-
error({ status: 404, path, referrer, referenceType: 'linked' });
|
|
544
|
+
async function visit(decoded, encoded, referrer) {
|
|
545
|
+
if (!decoded.startsWith(config.kit.paths.base)) {
|
|
546
|
+
error({ status: 404, path: decoded, referrer, referenceType: 'linked' });
|
|
533
547
|
return;
|
|
534
548
|
}
|
|
535
549
|
|
|
536
550
|
/** @type {Map<string, import('types/internal').PrerenderDependency>} */
|
|
537
551
|
const dependencies = new Map();
|
|
538
552
|
|
|
539
|
-
const
|
|
553
|
+
const response = await app.render(new Request(`http://sveltekit-prerender${encoded}`), {
|
|
540
554
|
prerender: {
|
|
541
555
|
all,
|
|
542
556
|
dependencies
|
|
543
557
|
}
|
|
544
558
|
});
|
|
545
559
|
|
|
546
|
-
const
|
|
547
|
-
|
|
560
|
+
const text = await response.text();
|
|
561
|
+
|
|
562
|
+
save(response, text, decoded, encoded, referrer, 'linked');
|
|
563
|
+
|
|
564
|
+
for (const [dependency_path, result] of dependencies) {
|
|
565
|
+
// this seems circuitous, but using new URL allows us to not care
|
|
566
|
+
// whether dependency_path is encoded or not
|
|
567
|
+
const encoded_dependency_path = new URL(dependency_path, 'http://localhost').pathname;
|
|
568
|
+
const decoded_dependency_path = decodeURI(encoded_dependency_path);
|
|
569
|
+
|
|
570
|
+
const body = result.body ?? new Uint8Array(await result.response.arrayBuffer());
|
|
571
|
+
save(
|
|
572
|
+
result.response,
|
|
573
|
+
body,
|
|
574
|
+
decoded_dependency_path,
|
|
575
|
+
encoded_dependency_path,
|
|
576
|
+
decoded,
|
|
577
|
+
'fetched'
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (config.kit.prerender.crawl && response.headers.get('content-type') === 'text/html') {
|
|
582
|
+
for (const href of crawl(text)) {
|
|
583
|
+
if (href.startsWith('data:') || href.startsWith('#')) continue;
|
|
584
|
+
|
|
585
|
+
const resolved = resolve(encoded, href);
|
|
586
|
+
if (!is_root_relative(resolved)) continue;
|
|
587
|
+
|
|
588
|
+
const parsed = new URL(resolved, 'http://localhost');
|
|
589
|
+
|
|
590
|
+
if (parsed.search) ;
|
|
591
|
+
|
|
592
|
+
const pathname = normalize_path(parsed.pathname, config.kit.trailingSlash);
|
|
593
|
+
enqueue(decoded, decodeURI(pathname), pathname);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* @param {Response} response
|
|
600
|
+
* @param {string | Uint8Array} body
|
|
601
|
+
* @param {string} decoded
|
|
602
|
+
* @param {string} encoded
|
|
603
|
+
* @param {string | null} referrer
|
|
604
|
+
* @param {'linked' | 'fetched'} referenceType
|
|
605
|
+
*/
|
|
606
|
+
function save(response, body, decoded, encoded, referrer, referenceType) {
|
|
607
|
+
const response_type = Math.floor(response.status / 100);
|
|
608
|
+
const type = /** @type {string} */ (response.headers.get('content-type'));
|
|
548
609
|
const is_html = response_type === REDIRECT || type === 'text/html';
|
|
549
610
|
|
|
550
|
-
const file =
|
|
611
|
+
const file = output_filename(decoded, is_html);
|
|
612
|
+
const dest = `${out}/${file}`;
|
|
613
|
+
|
|
614
|
+
if (written.has(file)) return;
|
|
615
|
+
written.add(file);
|
|
551
616
|
|
|
552
617
|
if (response_type === REDIRECT) {
|
|
553
|
-
const location =
|
|
618
|
+
const location = response.headers.get('location');
|
|
554
619
|
|
|
555
620
|
if (location) {
|
|
556
|
-
mkdirp(dirname(
|
|
621
|
+
mkdirp(dirname(dest));
|
|
557
622
|
|
|
558
|
-
log.warn(`${
|
|
623
|
+
log.warn(`${response.status} ${decoded} -> ${location}`);
|
|
559
624
|
|
|
560
625
|
writeFileSync(
|
|
561
|
-
|
|
626
|
+
dest,
|
|
562
627
|
`<meta http-equiv="refresh" content=${escape_html_attr(`0;url=${location}`)}>`
|
|
563
628
|
);
|
|
564
629
|
|
|
565
|
-
|
|
630
|
+
let resolved = resolve(encoded, location);
|
|
566
631
|
if (is_root_relative(resolved)) {
|
|
567
|
-
|
|
632
|
+
resolved = normalize_path(resolved, config.kit.trailingSlash);
|
|
633
|
+
enqueue(decoded, decodeURI(resolved), resolved);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (!prerendered.redirects.has(decoded)) {
|
|
637
|
+
prerendered.redirects.set(decoded, {
|
|
638
|
+
status: response.status,
|
|
639
|
+
location: resolved
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
prerendered.paths.push(normalize_path(decoded, 'never'));
|
|
568
643
|
}
|
|
569
644
|
} else {
|
|
570
|
-
log.warn(`location header missing on redirect received from ${
|
|
645
|
+
log.warn(`location header missing on redirect received from ${decoded}`);
|
|
571
646
|
}
|
|
572
647
|
|
|
573
648
|
return;
|
|
574
649
|
}
|
|
575
650
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
if (rendered.status === 200) {
|
|
579
|
-
mkdirp(dirname(file));
|
|
580
|
-
|
|
581
|
-
log.info(`${rendered.status} ${decoded_path}`);
|
|
582
|
-
writeFileSync(file, text);
|
|
583
|
-
paths.push(normalize(decoded_path));
|
|
584
|
-
} else if (response_type !== OK) {
|
|
585
|
-
error({ status: rendered.status, path, referrer, referenceType: 'linked' });
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
for (const [dependency_path, result] of dependencies) {
|
|
589
|
-
const { status, headers } = result.response;
|
|
590
|
-
|
|
591
|
-
const response_type = Math.floor(status / 100);
|
|
592
|
-
|
|
593
|
-
const is_html = headers.get('content-type') === 'text/html';
|
|
594
|
-
|
|
595
|
-
const file = `${out}${output_filename(dependency_path, is_html)}`;
|
|
596
|
-
mkdirp(dirname(file));
|
|
651
|
+
if (response.status === 200) {
|
|
652
|
+
mkdirp(dirname(dest));
|
|
597
653
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
result.body === null ? new Uint8Array(await result.response.arrayBuffer()) : result.body
|
|
601
|
-
);
|
|
602
|
-
paths.push(dependency_path);
|
|
654
|
+
log.info(`${response.status} ${decoded}`);
|
|
655
|
+
writeFileSync(dest, body);
|
|
603
656
|
|
|
604
|
-
if (
|
|
605
|
-
|
|
657
|
+
if (is_html) {
|
|
658
|
+
prerendered.pages.set(decoded, {
|
|
659
|
+
file
|
|
660
|
+
});
|
|
606
661
|
} else {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
path: dependency_path,
|
|
610
|
-
referrer: path,
|
|
611
|
-
referenceType: 'fetched'
|
|
662
|
+
prerendered.assets.set(decoded, {
|
|
663
|
+
type
|
|
612
664
|
});
|
|
613
665
|
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
if (is_html && config.kit.prerender.crawl) {
|
|
617
|
-
for (const href of crawl(text)) {
|
|
618
|
-
if (href.startsWith('data:') || href.startsWith('#')) continue;
|
|
619
666
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
const parsed = new URL(resolved, 'http://localhost');
|
|
624
|
-
|
|
625
|
-
let pathname = decodeURI(parsed.pathname);
|
|
626
|
-
|
|
627
|
-
if (config.kit.paths.base) {
|
|
628
|
-
if (!pathname.startsWith(config.kit.paths.base)) continue;
|
|
629
|
-
pathname = pathname.slice(config.kit.paths.base.length) || '/';
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
const file = pathname.slice(1);
|
|
633
|
-
if (files.has(file)) continue;
|
|
634
|
-
|
|
635
|
-
if (parsed.search) ;
|
|
636
|
-
|
|
637
|
-
enqueue(pathname, path);
|
|
638
|
-
}
|
|
667
|
+
prerendered.paths.push(normalize_path(decoded, 'never'));
|
|
668
|
+
} else if (response_type !== OK) {
|
|
669
|
+
error({ status: response.status, path: decoded, referrer, referenceType });
|
|
639
670
|
}
|
|
640
671
|
}
|
|
641
672
|
|
|
@@ -643,10 +674,10 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
643
674
|
for (const entry of config.kit.prerender.entries) {
|
|
644
675
|
if (entry === '*') {
|
|
645
676
|
for (const entry of build_data.entries) {
|
|
646
|
-
enqueue(config.kit.paths.base + entry,
|
|
677
|
+
enqueue(null, normalize_path(config.kit.paths.base + entry, config.kit.trailingSlash)); // TODO can we pre-normalize these?
|
|
647
678
|
}
|
|
648
679
|
} else {
|
|
649
|
-
enqueue(config.kit.paths.base + entry,
|
|
680
|
+
enqueue(null, normalize_path(config.kit.paths.base + entry, config.kit.trailingSlash));
|
|
650
681
|
}
|
|
651
682
|
}
|
|
652
683
|
|
|
@@ -667,9 +698,7 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
667
698
|
writeFileSync(file, await rendered.text());
|
|
668
699
|
}
|
|
669
700
|
|
|
670
|
-
return
|
|
671
|
-
paths
|
|
672
|
-
};
|
|
701
|
+
return prerendered;
|
|
673
702
|
}
|
|
674
703
|
|
|
675
704
|
/**
|
|
@@ -683,11 +712,14 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
683
712
|
*/
|
|
684
713
|
function create_builder({ cwd, config, build_data, log }) {
|
|
685
714
|
/** @type {Set<string>} */
|
|
686
|
-
|
|
715
|
+
let prerendered_paths;
|
|
716
|
+
|
|
687
717
|
let generated_manifest = false;
|
|
688
718
|
|
|
689
719
|
/** @param {import('types/internal').RouteData} route */
|
|
690
720
|
function not_prerendered(route) {
|
|
721
|
+
if (!prerendered_paths) return true;
|
|
722
|
+
|
|
691
723
|
if (route.type === 'page' && route.path) {
|
|
692
724
|
return !prerendered_paths.has(route.path);
|
|
693
725
|
}
|
|
@@ -832,10 +864,7 @@ function create_builder({ cwd, config, build_data, log }) {
|
|
|
832
864
|
log
|
|
833
865
|
});
|
|
834
866
|
|
|
835
|
-
prerendered.paths
|
|
836
|
-
prerendered_paths.add(path);
|
|
837
|
-
prerendered_paths.add(path + '/');
|
|
838
|
-
});
|
|
867
|
+
prerendered_paths = new Set(prerendered.paths);
|
|
839
868
|
|
|
840
869
|
return prerendered;
|
|
841
870
|
}
|
package/dist/cli.js
CHANGED
|
@@ -620,7 +620,8 @@ const options = object(
|
|
|
620
620
|
concurrency: number(1),
|
|
621
621
|
crawl: boolean(true),
|
|
622
622
|
createIndexFiles: error(
|
|
623
|
-
(keypath) =>
|
|
623
|
+
(keypath) =>
|
|
624
|
+
`${keypath} has been removed — it is now controlled by the trailingSlash option. See https://kit.svelte.dev/docs/configuration#trailingslash`
|
|
624
625
|
),
|
|
625
626
|
enabled: boolean(true),
|
|
626
627
|
entries: validate(['*'], (input, keypath) => {
|
|
@@ -677,7 +678,7 @@ const options = object(
|
|
|
677
678
|
|
|
678
679
|
serviceWorker: object({
|
|
679
680
|
register: boolean(true),
|
|
680
|
-
files: fun((filename) => !/\.
|
|
681
|
+
files: fun((filename) => !/\.DS_Store/.test(filename))
|
|
681
682
|
}),
|
|
682
683
|
|
|
683
684
|
// TODO remove this for 1.0
|
|
@@ -997,7 +998,7 @@ async function launch(port, https) {
|
|
|
997
998
|
exec(`${cmd} ${https ? 'https' : 'http'}://localhost:${port}`);
|
|
998
999
|
}
|
|
999
1000
|
|
|
1000
|
-
const prog = sade('svelte-kit').version('1.0.0-next.
|
|
1001
|
+
const prog = sade('svelte-kit').version('1.0.0-next.269');
|
|
1001
1002
|
|
|
1002
1003
|
prog
|
|
1003
1004
|
.command('dev')
|
|
@@ -1155,7 +1156,7 @@ async function check_port(port) {
|
|
|
1155
1156
|
function welcome({ port, host, https, open, loose, allow, cwd }) {
|
|
1156
1157
|
if (open) launch(port, https);
|
|
1157
1158
|
|
|
1158
|
-
console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.
|
|
1159
|
+
console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.269'}\n`));
|
|
1159
1160
|
|
|
1160
1161
|
const protocol = https ? 'https:' : 'http:';
|
|
1161
1162
|
const exposed = typeof host !== 'undefined' && host !== 'localhost' && host !== '127.0.0.1';
|
package/package.json
CHANGED
package/types/config.d.ts
CHANGED
|
@@ -35,6 +35,32 @@ export interface AdapterEntry {
|
|
|
35
35
|
}) => void;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
export interface Prerendered {
|
|
39
|
+
pages: Map<
|
|
40
|
+
string,
|
|
41
|
+
{
|
|
42
|
+
/** The location of the .html file relative to the output directory */
|
|
43
|
+
file: string;
|
|
44
|
+
}
|
|
45
|
+
>;
|
|
46
|
+
assets: Map<
|
|
47
|
+
string,
|
|
48
|
+
{
|
|
49
|
+
/** The MIME type of the asset */
|
|
50
|
+
type: string;
|
|
51
|
+
}
|
|
52
|
+
>;
|
|
53
|
+
redirects: Map<
|
|
54
|
+
string,
|
|
55
|
+
{
|
|
56
|
+
status: number;
|
|
57
|
+
location: string;
|
|
58
|
+
}
|
|
59
|
+
>;
|
|
60
|
+
/** An array of prerendered paths (without trailing slashes, regardless of the trailingSlash config) */
|
|
61
|
+
paths: string[];
|
|
62
|
+
}
|
|
63
|
+
|
|
38
64
|
export interface Builder {
|
|
39
65
|
log: Logger;
|
|
40
66
|
rimraf(dir: string): void;
|
|
@@ -87,9 +113,7 @@ export interface Builder {
|
|
|
87
113
|
}
|
|
88
114
|
): string[];
|
|
89
115
|
|
|
90
|
-
prerender(options: { all?: boolean; dest: string; fallback?: string }): Promise<
|
|
91
|
-
paths: string[];
|
|
92
|
-
}>;
|
|
116
|
+
prerender(options: { all?: boolean; dest: string; fallback?: string }): Promise<Prerendered>;
|
|
93
117
|
}
|
|
94
118
|
|
|
95
119
|
export interface Adapter {
|
package/types/index.d.ts
CHANGED
|
@@ -4,7 +4,14 @@
|
|
|
4
4
|
import './ambient-modules';
|
|
5
5
|
|
|
6
6
|
export { App, SSRManifest } from './app';
|
|
7
|
-
export {
|
|
7
|
+
export {
|
|
8
|
+
Adapter,
|
|
9
|
+
Builder,
|
|
10
|
+
Config,
|
|
11
|
+
Prerendered,
|
|
12
|
+
PrerenderErrorHandler,
|
|
13
|
+
ValidatedConfig
|
|
14
|
+
} from './config';
|
|
8
15
|
export { EndpointOutput, RequestHandler } from './endpoint';
|
|
9
16
|
export { ErrorLoad, ErrorLoadInput, Load, LoadInput, LoadOutput } from './page';
|
|
10
17
|
export { ExternalFetch, GetSession, Handle, HandleError, RequestEvent, ResolveOpts } from './hooks';
|