@sveltejs/kit 1.0.0-next.264 → 1.0.0-next.268
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 +67 -79
- package/dist/chunks/index5.js +173 -141
- package/dist/cli.js +7 -4
- package/package.json +3 -2
- package/types/config.d.ts +28 -4
- 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;
|
|
@@ -1698,9 +1693,7 @@ async function load_node({
|
|
|
1698
1693
|
} else {
|
|
1699
1694
|
// external
|
|
1700
1695
|
if (resolved.startsWith('//')) {
|
|
1701
|
-
|
|
1702
|
-
`Cannot request protocol-relative URL (${requested}) in server-side fetch`
|
|
1703
|
-
);
|
|
1696
|
+
requested = event.url.protocol + requested;
|
|
1704
1697
|
}
|
|
1705
1698
|
|
|
1706
1699
|
// external fetch
|
|
@@ -1742,11 +1735,21 @@ async function load_node({
|
|
|
1742
1735
|
}
|
|
1743
1736
|
|
|
1744
1737
|
if (!opts.body || typeof opts.body === 'string') {
|
|
1738
|
+
// the json constructed below is later added to the dom in a script tag
|
|
1739
|
+
// make sure the used values are safe
|
|
1740
|
+
const status_number = Number(response.status);
|
|
1741
|
+
if (isNaN(status_number)) {
|
|
1742
|
+
throw new Error(
|
|
1743
|
+
`response.status is not a number. value: "${
|
|
1744
|
+
response.status
|
|
1745
|
+
}" type: ${typeof response.status}`
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1745
1748
|
// prettier-ignore
|
|
1746
1749
|
fetched.push({
|
|
1747
1750
|
url: requested,
|
|
1748
1751
|
body: /** @type {string} */ (opts.body),
|
|
1749
|
-
json: `{"status":${
|
|
1752
|
+
json: `{"status":${status_number},"statusText":${s(response.statusText)},"headers":${s(headers)},"body":${escape_json_in_html(body)}}`
|
|
1750
1753
|
});
|
|
1751
1754
|
}
|
|
1752
1755
|
|
|
@@ -1887,22 +1890,21 @@ async function load_shadow_data(route, event, prerender) {
|
|
|
1887
1890
|
if (result.fallthrough) return result;
|
|
1888
1891
|
|
|
1889
1892
|
const { status, headers, body } = validate_shadow_output(result);
|
|
1893
|
+
data.status = status;
|
|
1894
|
+
|
|
1890
1895
|
add_cookies(/** @type {string[]} */ (data.cookies), headers);
|
|
1891
1896
|
|
|
1892
1897
|
// Redirects are respected...
|
|
1893
1898
|
if (status >= 300 && status < 400) {
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
)
|
|
1899
|
-
};
|
|
1899
|
+
data.redirect = /** @type {string} */ (
|
|
1900
|
+
headers instanceof Headers ? headers.get('location') : headers.location
|
|
1901
|
+
);
|
|
1902
|
+
return data;
|
|
1900
1903
|
}
|
|
1901
1904
|
|
|
1902
1905
|
// ...but 4xx and 5xx status codes _don't_ result in the error page
|
|
1903
1906
|
// rendering for non-GET requests — instead, we allow the page
|
|
1904
1907
|
// to render with any validation errors etc that were returned
|
|
1905
|
-
data.status = status;
|
|
1906
1908
|
data.body = body;
|
|
1907
1909
|
}
|
|
1908
1910
|
|
|
@@ -1913,21 +1915,18 @@ async function load_shadow_data(route, event, prerender) {
|
|
|
1913
1915
|
|
|
1914
1916
|
const { status, headers, body } = validate_shadow_output(result);
|
|
1915
1917
|
add_cookies(/** @type {string[]} */ (data.cookies), headers);
|
|
1918
|
+
data.status = status;
|
|
1916
1919
|
|
|
1917
1920
|
if (status >= 400) {
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
error: new Error('Failed to load data')
|
|
1921
|
-
};
|
|
1921
|
+
data.error = new Error('Failed to load data');
|
|
1922
|
+
return data;
|
|
1922
1923
|
}
|
|
1923
1924
|
|
|
1924
1925
|
if (status >= 300) {
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
)
|
|
1930
|
-
};
|
|
1926
|
+
data.redirect = /** @type {string} */ (
|
|
1927
|
+
headers instanceof Headers ? headers.get('location') : headers.location
|
|
1928
|
+
);
|
|
1929
|
+
return data;
|
|
1931
1930
|
}
|
|
1932
1931
|
|
|
1933
1932
|
data.body = { ...body, ...data.body };
|
|
@@ -2446,26 +2445,15 @@ const DATA_SUFFIX = '/__data.json';
|
|
|
2446
2445
|
async function respond(request, options, state = {}) {
|
|
2447
2446
|
const url = new URL(request.url);
|
|
2448
2447
|
|
|
2449
|
-
|
|
2450
|
-
const has_trailing_slash = url.pathname.endsWith('/');
|
|
2448
|
+
const normalized = normalize_path(url.pathname, options.trailing_slash);
|
|
2451
2449
|
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
if (url.search === '?') url.search = '';
|
|
2461
|
-
|
|
2462
|
-
return new Response(undefined, {
|
|
2463
|
-
status: 301,
|
|
2464
|
-
headers: {
|
|
2465
|
-
location: url.pathname + url.search
|
|
2466
|
-
}
|
|
2467
|
-
});
|
|
2468
|
-
}
|
|
2450
|
+
if (normalized !== url.pathname) {
|
|
2451
|
+
return new Response(undefined, {
|
|
2452
|
+
status: 301,
|
|
2453
|
+
headers: {
|
|
2454
|
+
location: normalized + (url.search === '?' ? '' : url.search)
|
|
2455
|
+
}
|
|
2456
|
+
});
|
|
2469
2457
|
}
|
|
2470
2458
|
|
|
2471
2459
|
const { parameter, allowed } = options.method_override;
|
|
@@ -2594,11 +2582,11 @@ async function respond(request, options, state = {}) {
|
|
|
2594
2582
|
const location = response.headers.get('location');
|
|
2595
2583
|
|
|
2596
2584
|
if (location) {
|
|
2585
|
+
const headers = new Headers(response.headers);
|
|
2586
|
+
headers.set('x-sveltekit-location', location);
|
|
2597
2587
|
response = new Response(undefined, {
|
|
2598
2588
|
status: 204,
|
|
2599
|
-
headers
|
|
2600
|
-
'x-sveltekit-location': location
|
|
2601
|
-
}
|
|
2589
|
+
headers
|
|
2602
2590
|
});
|
|
2603
2591
|
}
|
|
2604
2592
|
}
|
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
|
|
@@ -391,21 +420,21 @@ function escape_html_attr(str) {
|
|
|
391
420
|
* @typedef {import('types/internal').Logger} Logger
|
|
392
421
|
*/
|
|
393
422
|
|
|
394
|
-
/** @type {(
|
|
395
|
-
function
|
|
423
|
+
/** @type {(details: Parameters<PrerenderErrorHandler>[0] ) => string} */
|
|
424
|
+
function format_error({ status, path, referrer, referenceType }) {
|
|
396
425
|
return `${status} ${path}${referrer ? ` (${referenceType} from ${referrer})` : ''}`;
|
|
397
426
|
}
|
|
398
427
|
|
|
399
428
|
/** @type {(log: Logger, onError: OnError) => PrerenderErrorHandler} */
|
|
400
|
-
function
|
|
429
|
+
function normalise_error_handler(log, onError) {
|
|
401
430
|
switch (onError) {
|
|
402
431
|
case 'continue':
|
|
403
|
-
return (
|
|
404
|
-
log.error(
|
|
432
|
+
return (details) => {
|
|
433
|
+
log.error(format_error(details));
|
|
405
434
|
};
|
|
406
435
|
case 'fail':
|
|
407
|
-
return (
|
|
408
|
-
throw new Error(
|
|
436
|
+
return (details) => {
|
|
437
|
+
throw new Error(format_error(details));
|
|
409
438
|
};
|
|
410
439
|
default:
|
|
411
440
|
return onError;
|
|
@@ -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,11 +482,9 @@ 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
|
-
const error =
|
|
487
|
+
const error = normalise_error_handler(log, config.kit.prerender.onError);
|
|
459
488
|
|
|
460
489
|
const files = new Set([
|
|
461
490
|
...build_data.static,
|
|
@@ -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,148 +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
|
-
|
|
496
|
-
|
|
508
|
+
const file = path.slice(config.kit.paths.base.length + 1);
|
|
509
|
+
|
|
510
|
+
if (file === '') {
|
|
511
|
+
return 'index.html';
|
|
497
512
|
}
|
|
498
|
-
|
|
499
|
-
if (is_html &&
|
|
500
|
-
|
|
501
|
-
parts.push('index.html');
|
|
502
|
-
} else {
|
|
503
|
-
parts[parts.length - 1] += '.html';
|
|
504
|
-
}
|
|
513
|
+
|
|
514
|
+
if (is_html && !file.endsWith('.html')) {
|
|
515
|
+
return file + (config.kit.trailingSlash === 'always' ? 'index.html' : '.html');
|
|
505
516
|
}
|
|
506
|
-
|
|
517
|
+
|
|
518
|
+
return file;
|
|
507
519
|
}
|
|
508
520
|
|
|
521
|
+
const seen = new Set();
|
|
522
|
+
const written = new Set();
|
|
523
|
+
|
|
509
524
|
/**
|
|
510
|
-
* @param {string}
|
|
511
|
-
* @param {string
|
|
525
|
+
* @param {string | null} referrer
|
|
526
|
+
* @param {string} decoded
|
|
527
|
+
* @param {string} [encoded]
|
|
512
528
|
*/
|
|
513
|
-
function enqueue(
|
|
514
|
-
|
|
529
|
+
function enqueue(referrer, decoded, encoded) {
|
|
530
|
+
if (seen.has(decoded)) return;
|
|
531
|
+
seen.add(decoded);
|
|
515
532
|
|
|
516
|
-
|
|
517
|
-
|
|
533
|
+
const file = decoded.slice(config.kit.paths.base.length + 1);
|
|
534
|
+
if (files.has(file)) return;
|
|
518
535
|
|
|
519
|
-
return q.add(() => visit(
|
|
536
|
+
return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer));
|
|
520
537
|
}
|
|
521
538
|
|
|
522
539
|
/**
|
|
523
|
-
* @param {string}
|
|
524
|
-
* @param {string}
|
|
540
|
+
* @param {string} decoded
|
|
541
|
+
* @param {string} encoded
|
|
525
542
|
* @param {string?} referrer
|
|
526
543
|
*/
|
|
527
|
-
async function visit(
|
|
544
|
+
async function visit(decoded, encoded, referrer) {
|
|
545
|
+
if (!decoded.startsWith(config.kit.paths.base)) {
|
|
546
|
+
error({ status: 404, path: decoded, referrer, referenceType: 'linked' });
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
528
550
|
/** @type {Map<string, import('types/internal').PrerenderDependency>} */
|
|
529
551
|
const dependencies = new Map();
|
|
530
552
|
|
|
531
|
-
const
|
|
532
|
-
? `http://sveltekit-prerender${config.kit.paths.base}${path === '/' ? '' : path}`
|
|
533
|
-
: `http://sveltekit-prerender${path}`;
|
|
534
|
-
|
|
535
|
-
const rendered = await app.render(new Request(render_path), {
|
|
553
|
+
const response = await app.render(new Request(`http://sveltekit-prerender${encoded}`), {
|
|
536
554
|
prerender: {
|
|
537
555
|
all,
|
|
538
556
|
dependencies
|
|
539
557
|
}
|
|
540
558
|
});
|
|
541
559
|
|
|
542
|
-
|
|
543
|
-
const response_type = Math.floor(rendered.status / 100);
|
|
544
|
-
const type = rendered.headers.get('content-type');
|
|
545
|
-
const is_html = response_type === REDIRECT || type === 'text/html';
|
|
560
|
+
const text = await response.text();
|
|
546
561
|
|
|
547
|
-
|
|
562
|
+
save(response, text, decoded, encoded, referrer, 'linked');
|
|
548
563
|
|
|
549
|
-
|
|
550
|
-
|
|
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);
|
|
551
569
|
|
|
552
|
-
|
|
553
|
-
|
|
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
|
+
}
|
|
554
580
|
|
|
555
|
-
|
|
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;
|
|
556
584
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
`<meta http-equiv="refresh" content=${escape_html_attr(`0;url=${location}`)}>`
|
|
560
|
-
);
|
|
585
|
+
const resolved = resolve(encoded, href);
|
|
586
|
+
if (!is_root_relative(resolved)) continue;
|
|
561
587
|
|
|
562
|
-
|
|
563
|
-
if (is_root_relative(resolved)) {
|
|
564
|
-
enqueue(resolved, path);
|
|
565
|
-
}
|
|
566
|
-
} else {
|
|
567
|
-
log.warn(`location header missing on redirect received from ${decoded_path}`);
|
|
568
|
-
}
|
|
588
|
+
const parsed = new URL(resolved, 'http://localhost');
|
|
569
589
|
|
|
570
|
-
|
|
571
|
-
}
|
|
590
|
+
if (parsed.search) ;
|
|
572
591
|
|
|
573
|
-
|
|
592
|
+
const pathname = normalize_path(parsed.pathname, config.kit.trailingSlash);
|
|
593
|
+
enqueue(decoded, decodeURI(pathname), pathname);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
574
597
|
|
|
575
|
-
|
|
576
|
-
|
|
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'));
|
|
609
|
+
const is_html = response_type === REDIRECT || type === 'text/html';
|
|
577
610
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
paths.push(normalize(decoded_path));
|
|
581
|
-
} else if (response_type !== OK) {
|
|
582
|
-
error({ status: rendered.status, path, referrer, referenceType: 'linked' });
|
|
583
|
-
}
|
|
611
|
+
const file = output_filename(decoded, is_html);
|
|
612
|
+
const dest = `${out}/${file}`;
|
|
584
613
|
|
|
585
|
-
|
|
586
|
-
|
|
614
|
+
if (written.has(file)) return;
|
|
615
|
+
written.add(file);
|
|
587
616
|
|
|
588
|
-
|
|
617
|
+
if (response_type === REDIRECT) {
|
|
618
|
+
const location = response.headers.get('location');
|
|
589
619
|
|
|
590
|
-
|
|
620
|
+
if (location) {
|
|
621
|
+
mkdirp(dirname(dest));
|
|
591
622
|
|
|
592
|
-
|
|
593
|
-
mkdirp(dirname(file));
|
|
623
|
+
log.warn(`${response.status} ${decoded} -> ${location}`);
|
|
594
624
|
|
|
595
625
|
writeFileSync(
|
|
596
|
-
|
|
597
|
-
|
|
626
|
+
dest,
|
|
627
|
+
`<meta http-equiv="refresh" content=${escape_html_attr(`0;url=${location}`)}>`
|
|
598
628
|
);
|
|
599
|
-
paths.push(dependency_path);
|
|
600
|
-
|
|
601
|
-
if (response_type === OK) {
|
|
602
|
-
log.info(`${status} ${dependency_path}`);
|
|
603
|
-
} else {
|
|
604
|
-
error({
|
|
605
|
-
status,
|
|
606
|
-
path: dependency_path,
|
|
607
|
-
referrer: path,
|
|
608
|
-
referenceType: 'fetched'
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
629
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
if (!is_root_relative(resolved)) continue;
|
|
630
|
+
let resolved = resolve(encoded, location);
|
|
631
|
+
if (is_root_relative(resolved)) {
|
|
632
|
+
resolved = normalize_path(resolved, config.kit.trailingSlash);
|
|
633
|
+
enqueue(decoded, decodeURI(resolved), resolved);
|
|
634
|
+
}
|
|
619
635
|
|
|
620
|
-
|
|
636
|
+
if (!prerendered.redirects.has(decoded)) {
|
|
637
|
+
prerendered.redirects.set(decoded, {
|
|
638
|
+
status: response.status,
|
|
639
|
+
location: resolved
|
|
640
|
+
});
|
|
621
641
|
|
|
622
|
-
|
|
642
|
+
prerendered.paths.push(normalize_path(decoded, 'never'));
|
|
643
|
+
}
|
|
644
|
+
} else {
|
|
645
|
+
log.warn(`location header missing on redirect received from ${decoded}`);
|
|
646
|
+
}
|
|
623
647
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
pathname = pathname.slice(config.kit.paths.base.length) || '/';
|
|
627
|
-
}
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
628
650
|
|
|
629
|
-
|
|
630
|
-
|
|
651
|
+
if (response.status === 200) {
|
|
652
|
+
mkdirp(dirname(dest));
|
|
631
653
|
|
|
632
|
-
|
|
654
|
+
log.info(`${response.status} ${decoded}`);
|
|
655
|
+
writeFileSync(dest, body);
|
|
633
656
|
|
|
634
|
-
|
|
635
|
-
|
|
657
|
+
if (is_html) {
|
|
658
|
+
prerendered.pages.set(decoded, {
|
|
659
|
+
file
|
|
660
|
+
});
|
|
661
|
+
} else {
|
|
662
|
+
prerendered.assets.set(decoded, {
|
|
663
|
+
type
|
|
664
|
+
});
|
|
636
665
|
}
|
|
666
|
+
|
|
667
|
+
prerendered.paths.push(normalize_path(decoded, 'never'));
|
|
668
|
+
} else if (response_type !== OK) {
|
|
669
|
+
error({ status: response.status, path: decoded, referrer, referenceType });
|
|
637
670
|
}
|
|
638
671
|
}
|
|
639
672
|
|
|
@@ -641,10 +674,10 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
641
674
|
for (const entry of config.kit.prerender.entries) {
|
|
642
675
|
if (entry === '*') {
|
|
643
676
|
for (const entry of build_data.entries) {
|
|
644
|
-
enqueue(entry,
|
|
677
|
+
enqueue(null, normalize_path(config.kit.paths.base + entry, config.kit.trailingSlash)); // TODO can we pre-normalize these?
|
|
645
678
|
}
|
|
646
679
|
} else {
|
|
647
|
-
enqueue(entry,
|
|
680
|
+
enqueue(null, normalize_path(config.kit.paths.base + entry, config.kit.trailingSlash));
|
|
648
681
|
}
|
|
649
682
|
}
|
|
650
683
|
|
|
@@ -665,9 +698,7 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
665
698
|
writeFileSync(file, await rendered.text());
|
|
666
699
|
}
|
|
667
700
|
|
|
668
|
-
return
|
|
669
|
-
paths
|
|
670
|
-
};
|
|
701
|
+
return prerendered;
|
|
671
702
|
}
|
|
672
703
|
|
|
673
704
|
/**
|
|
@@ -681,11 +712,14 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
681
712
|
*/
|
|
682
713
|
function create_builder({ cwd, config, build_data, log }) {
|
|
683
714
|
/** @type {Set<string>} */
|
|
684
|
-
|
|
715
|
+
let prerendered_paths;
|
|
716
|
+
|
|
685
717
|
let generated_manifest = false;
|
|
686
718
|
|
|
687
719
|
/** @param {import('types/internal').RouteData} route */
|
|
688
720
|
function not_prerendered(route) {
|
|
721
|
+
if (!prerendered_paths) return true;
|
|
722
|
+
|
|
689
723
|
if (route.type === 'page' && route.path) {
|
|
690
724
|
return !prerendered_paths.has(route.path);
|
|
691
725
|
}
|
|
@@ -700,6 +734,7 @@ function create_builder({ cwd, config, build_data, log }) {
|
|
|
700
734
|
copy,
|
|
701
735
|
|
|
702
736
|
appDir: config.kit.appDir,
|
|
737
|
+
trailingSlash: config.kit.trailingSlash,
|
|
703
738
|
|
|
704
739
|
createEntries(fn) {
|
|
705
740
|
generated_manifest = true;
|
|
@@ -829,10 +864,7 @@ function create_builder({ cwd, config, build_data, log }) {
|
|
|
829
864
|
log
|
|
830
865
|
});
|
|
831
866
|
|
|
832
|
-
prerendered.paths
|
|
833
|
-
prerendered_paths.add(path);
|
|
834
|
-
prerendered_paths.add(path + '/');
|
|
835
|
-
});
|
|
867
|
+
prerendered_paths = new Set(prerendered.paths);
|
|
836
868
|
|
|
837
869
|
return prerendered;
|
|
838
870
|
}
|
package/dist/cli.js
CHANGED
|
@@ -619,7 +619,10 @@ const options = object(
|
|
|
619
619
|
prerender: object({
|
|
620
620
|
concurrency: number(1),
|
|
621
621
|
crawl: boolean(true),
|
|
622
|
-
createIndexFiles:
|
|
622
|
+
createIndexFiles: error(
|
|
623
|
+
(keypath) =>
|
|
624
|
+
`${keypath} has been removed — it is now controlled by the trailingSlash option. See https://kit.svelte.dev/docs/configuration#trailingslash`
|
|
625
|
+
),
|
|
623
626
|
enabled: boolean(true),
|
|
624
627
|
entries: validate(['*'], (input, keypath) => {
|
|
625
628
|
if (!Array.isArray(input) || !input.every((page) => typeof page === 'string')) {
|
|
@@ -675,7 +678,7 @@ const options = object(
|
|
|
675
678
|
|
|
676
679
|
serviceWorker: object({
|
|
677
680
|
register: boolean(true),
|
|
678
|
-
files: fun((filename) => !/\.
|
|
681
|
+
files: fun((filename) => !/\.DS_Store/.test(filename))
|
|
679
682
|
}),
|
|
680
683
|
|
|
681
684
|
// TODO remove this for 1.0
|
|
@@ -995,7 +998,7 @@ async function launch(port, https) {
|
|
|
995
998
|
exec(`${cmd} ${https ? 'https' : 'http'}://localhost:${port}`);
|
|
996
999
|
}
|
|
997
1000
|
|
|
998
|
-
const prog = sade('svelte-kit').version('1.0.0-next.
|
|
1001
|
+
const prog = sade('svelte-kit').version('1.0.0-next.268');
|
|
999
1002
|
|
|
1000
1003
|
prog
|
|
1001
1004
|
.command('dev')
|
|
@@ -1153,7 +1156,7 @@ async function check_port(port) {
|
|
|
1153
1156
|
function welcome({ port, host, https, open, loose, allow, cwd }) {
|
|
1154
1157
|
if (open) launch(port, https);
|
|
1155
1158
|
|
|
1156
|
-
console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.
|
|
1159
|
+
console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.268'}\n`));
|
|
1157
1160
|
|
|
1158
1161
|
const protocol = https ? 'https:' : 'http:';
|
|
1159
1162
|
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.
|
|
3
|
+
"version": "1.0.0-next.268",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/sveltejs/kit",
|
|
@@ -84,8 +84,9 @@
|
|
|
84
84
|
"check-format": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore",
|
|
85
85
|
"test": "npm run test:unit && npm run test:packaging && npm run test:prerendering && npm run test:integration",
|
|
86
86
|
"test:unit": "uvu src \"(spec\\.js|test[\\\\/]index\\.js)\" -i packaging",
|
|
87
|
-
"test:prerendering": "pnpm test:prerendering:basics",
|
|
87
|
+
"test:prerendering": "pnpm test:prerendering:basics && pnpm test:prerendering:options",
|
|
88
88
|
"test:prerendering:basics": "cd test/prerendering/basics && pnpm test",
|
|
89
|
+
"test:prerendering:options": "cd test/prerendering/options && pnpm test",
|
|
89
90
|
"test:packaging": "uvu src/packaging \"(spec\\.js|test[\\\\/]index\\.js)\"",
|
|
90
91
|
"test:integration": "pnpm test:integration:amp && pnpm test:integration:basics && pnpm test:integration:options && pnpm test:integration:options-2",
|
|
91
92
|
"test:integration:amp": "cd test/apps/amp && pnpm test",
|
package/types/config.d.ts
CHANGED
|
@@ -35,12 +35,39 @@ 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;
|
|
41
67
|
mkdirp(dir: string): void;
|
|
42
68
|
|
|
43
69
|
appDir: string;
|
|
70
|
+
trailingSlash: 'always' | 'never' | 'ignore';
|
|
44
71
|
|
|
45
72
|
/**
|
|
46
73
|
* Create entry points that map to individual functions
|
|
@@ -86,9 +113,7 @@ export interface Builder {
|
|
|
86
113
|
}
|
|
87
114
|
): string[];
|
|
88
115
|
|
|
89
|
-
prerender(options: { all?: boolean; dest: string; fallback?: string }): Promise<
|
|
90
|
-
paths: string[];
|
|
91
|
-
}>;
|
|
116
|
+
prerender(options: { all?: boolean; dest: string; fallback?: string }): Promise<Prerendered>;
|
|
92
117
|
}
|
|
93
118
|
|
|
94
119
|
export interface Adapter {
|
|
@@ -153,7 +178,6 @@ export interface Config {
|
|
|
153
178
|
prerender?: {
|
|
154
179
|
concurrency?: number;
|
|
155
180
|
crawl?: boolean;
|
|
156
|
-
createIndexFiles?: boolean;
|
|
157
181
|
enabled?: boolean;
|
|
158
182
|
entries?: string[];
|
|
159
183
|
onError?: PrerenderOnErrorValue;
|
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';
|