@sveltejs/kit 1.0.0-next.263 → 1.0.0-next.267
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 -12
- package/assets/server/index.js +46 -45
- package/dist/chunks/index.js +1 -1
- package/dist/chunks/index5.js +160 -141
- package/dist/chunks/index7.js +11 -9
- package/dist/cli.js +16 -13
- 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
|
|
|
@@ -1464,10 +1473,6 @@ class Renderer {
|
|
|
1464
1473
|
* }} opts
|
|
1465
1474
|
*/
|
|
1466
1475
|
async function start({ paths, target, session, route, spa, trailing_slash, hydrate }) {
|
|
1467
|
-
if (import.meta.env.DEV && !target) {
|
|
1468
|
-
throw new Error('Missing target element. See https://kit.svelte.dev/docs#configuration-target');
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
1476
|
const renderer = new Renderer({
|
|
1472
1477
|
Root,
|
|
1473
1478
|
fallback,
|
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
|
|
|
@@ -1496,6 +1498,22 @@ function is_root_relative(path) {
|
|
|
1496
1498
|
return path[0] === '/' && path[1] !== '/';
|
|
1497
1499
|
}
|
|
1498
1500
|
|
|
1501
|
+
/**
|
|
1502
|
+
* @param {string} path
|
|
1503
|
+
* @param {'always' | 'never' | 'ignore'} trailing_slash
|
|
1504
|
+
*/
|
|
1505
|
+
function normalize_path(path, trailing_slash) {
|
|
1506
|
+
if (path === '/' || trailing_slash === 'ignore') return path;
|
|
1507
|
+
|
|
1508
|
+
if (trailing_slash === 'never') {
|
|
1509
|
+
return path.endsWith('/') ? path.slice(0, -1) : path;
|
|
1510
|
+
} else if (trailing_slash === 'always' && /\/[^./]+$/.test(path)) {
|
|
1511
|
+
return path + '/';
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
return path;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1499
1517
|
/**
|
|
1500
1518
|
* @param {{
|
|
1501
1519
|
* event: import('types/hooks').RequestEvent;
|
|
@@ -1698,9 +1716,7 @@ async function load_node({
|
|
|
1698
1716
|
} else {
|
|
1699
1717
|
// external
|
|
1700
1718
|
if (resolved.startsWith('//')) {
|
|
1701
|
-
|
|
1702
|
-
`Cannot request protocol-relative URL (${requested}) in server-side fetch`
|
|
1703
|
-
);
|
|
1719
|
+
requested = event.url.protocol + requested;
|
|
1704
1720
|
}
|
|
1705
1721
|
|
|
1706
1722
|
// external fetch
|
|
@@ -1887,22 +1903,21 @@ async function load_shadow_data(route, event, prerender) {
|
|
|
1887
1903
|
if (result.fallthrough) return result;
|
|
1888
1904
|
|
|
1889
1905
|
const { status, headers, body } = validate_shadow_output(result);
|
|
1906
|
+
data.status = status;
|
|
1907
|
+
|
|
1890
1908
|
add_cookies(/** @type {string[]} */ (data.cookies), headers);
|
|
1891
1909
|
|
|
1892
1910
|
// Redirects are respected...
|
|
1893
1911
|
if (status >= 300 && status < 400) {
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
)
|
|
1899
|
-
};
|
|
1912
|
+
data.redirect = /** @type {string} */ (
|
|
1913
|
+
headers instanceof Headers ? headers.get('location') : headers.location
|
|
1914
|
+
);
|
|
1915
|
+
return data;
|
|
1900
1916
|
}
|
|
1901
1917
|
|
|
1902
1918
|
// ...but 4xx and 5xx status codes _don't_ result in the error page
|
|
1903
1919
|
// rendering for non-GET requests — instead, we allow the page
|
|
1904
1920
|
// to render with any validation errors etc that were returned
|
|
1905
|
-
data.status = status;
|
|
1906
1921
|
data.body = body;
|
|
1907
1922
|
}
|
|
1908
1923
|
|
|
@@ -1913,21 +1928,18 @@ async function load_shadow_data(route, event, prerender) {
|
|
|
1913
1928
|
|
|
1914
1929
|
const { status, headers, body } = validate_shadow_output(result);
|
|
1915
1930
|
add_cookies(/** @type {string[]} */ (data.cookies), headers);
|
|
1931
|
+
data.status = status;
|
|
1916
1932
|
|
|
1917
1933
|
if (status >= 400) {
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
error: new Error('Failed to load data')
|
|
1921
|
-
};
|
|
1934
|
+
data.error = new Error('Failed to load data');
|
|
1935
|
+
return data;
|
|
1922
1936
|
}
|
|
1923
1937
|
|
|
1924
1938
|
if (status >= 300) {
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
)
|
|
1930
|
-
};
|
|
1939
|
+
data.redirect = /** @type {string} */ (
|
|
1940
|
+
headers instanceof Headers ? headers.get('location') : headers.location
|
|
1941
|
+
);
|
|
1942
|
+
return data;
|
|
1931
1943
|
}
|
|
1932
1944
|
|
|
1933
1945
|
data.body = { ...body, ...data.body };
|
|
@@ -2311,7 +2323,7 @@ function get_page_config(leaf, options) {
|
|
|
2311
2323
|
// TODO remove for 1.0
|
|
2312
2324
|
if ('ssr' in leaf) {
|
|
2313
2325
|
throw new Error(
|
|
2314
|
-
'`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs#
|
|
2326
|
+
'`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle'
|
|
2315
2327
|
);
|
|
2316
2328
|
}
|
|
2317
2329
|
|
|
@@ -2446,26 +2458,15 @@ const DATA_SUFFIX = '/__data.json';
|
|
|
2446
2458
|
async function respond(request, options, state = {}) {
|
|
2447
2459
|
const url = new URL(request.url);
|
|
2448
2460
|
|
|
2449
|
-
|
|
2450
|
-
const has_trailing_slash = url.pathname.endsWith('/');
|
|
2461
|
+
const normalized = normalize_path(url.pathname, options.trailing_slash);
|
|
2451
2462
|
|
|
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
|
-
}
|
|
2463
|
+
if (normalized !== url.pathname) {
|
|
2464
|
+
return new Response(undefined, {
|
|
2465
|
+
status: 301,
|
|
2466
|
+
headers: {
|
|
2467
|
+
location: normalized + (url.search === '?' ? '' : url.search)
|
|
2468
|
+
}
|
|
2469
|
+
});
|
|
2469
2470
|
}
|
|
2470
2471
|
|
|
2471
2472
|
const { parameter, allowed } = options.method_override;
|
|
@@ -2482,7 +2483,7 @@ async function respond(request, options, state = {}) {
|
|
|
2482
2483
|
});
|
|
2483
2484
|
} else {
|
|
2484
2485
|
const verb = allowed.length === 0 ? 'enabled' : 'allowed';
|
|
2485
|
-
const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs#
|
|
2486
|
+
const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs/configuration#methodoverride`;
|
|
2486
2487
|
|
|
2487
2488
|
return new Response(body, {
|
|
2488
2489
|
status: 400
|
|
@@ -2594,11 +2595,11 @@ async function respond(request, options, state = {}) {
|
|
|
2594
2595
|
const location = response.headers.get('location');
|
|
2595
2596
|
|
|
2596
2597
|
if (location) {
|
|
2598
|
+
const headers = new Headers(response.headers);
|
|
2599
|
+
headers.set('x-sveltekit-location', location);
|
|
2597
2600
|
response = new Response(undefined, {
|
|
2598
2601
|
status: 204,
|
|
2599
|
-
headers
|
|
2600
|
-
'x-sveltekit-location': location
|
|
2601
|
-
}
|
|
2602
|
+
headers
|
|
2602
2603
|
});
|
|
2603
2604
|
}
|
|
2604
2605
|
}
|
package/dist/chunks/index.js
CHANGED
|
@@ -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
|
|
214
|
+
'The getContext hook has been removed. See https://kit.svelte.dev/docs/hooks'
|
|
215
215
|
);
|
|
216
216
|
}
|
|
217
217
|
|
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,
|
|
@@ -391,21 +407,21 @@ function escape_html_attr(str) {
|
|
|
391
407
|
* @typedef {import('types/internal').Logger} Logger
|
|
392
408
|
*/
|
|
393
409
|
|
|
394
|
-
/** @type {(
|
|
395
|
-
function
|
|
410
|
+
/** @type {(details: Parameters<PrerenderErrorHandler>[0] ) => string} */
|
|
411
|
+
function format_error({ status, path, referrer, referenceType }) {
|
|
396
412
|
return `${status} ${path}${referrer ? ` (${referenceType} from ${referrer})` : ''}`;
|
|
397
413
|
}
|
|
398
414
|
|
|
399
415
|
/** @type {(log: Logger, onError: OnError) => PrerenderErrorHandler} */
|
|
400
|
-
function
|
|
416
|
+
function normalise_error_handler(log, onError) {
|
|
401
417
|
switch (onError) {
|
|
402
418
|
case 'continue':
|
|
403
|
-
return (
|
|
404
|
-
log.error(
|
|
419
|
+
return (details) => {
|
|
420
|
+
log.error(format_error(details));
|
|
405
421
|
};
|
|
406
422
|
case 'fail':
|
|
407
|
-
return (
|
|
408
|
-
throw new Error(
|
|
423
|
+
return (details) => {
|
|
424
|
+
throw new Error(format_error(details));
|
|
409
425
|
};
|
|
410
426
|
default:
|
|
411
427
|
return onError;
|
|
@@ -425,25 +441,27 @@ const REDIRECT = 3;
|
|
|
425
441
|
* fallback?: string;
|
|
426
442
|
* all: boolean; // disregard `export const prerender = true`
|
|
427
443
|
* }} 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
444
|
*/
|
|
430
445
|
async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
446
|
+
/** @type {import('types/config').Prerendered} */
|
|
447
|
+
const prerendered = {
|
|
448
|
+
pages: new Map(),
|
|
449
|
+
assets: new Map(),
|
|
450
|
+
redirects: new Map(),
|
|
451
|
+
paths: []
|
|
452
|
+
};
|
|
453
|
+
|
|
431
454
|
if (!config.kit.prerender.enabled && !fallback) {
|
|
432
|
-
return
|
|
455
|
+
return prerendered;
|
|
433
456
|
}
|
|
434
457
|
|
|
435
458
|
__fetch_polyfill();
|
|
436
459
|
|
|
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);
|
|
460
|
+
const server_root = resolve$1(cwd, `${SVELTE_KIT}/output`);
|
|
444
461
|
|
|
445
462
|
/** @type {import('types/internal').AppModule} */
|
|
446
463
|
const { App, override } = await import(pathToFileURL(`${server_root}/server/app.js`).href);
|
|
464
|
+
const { manifest } = await import(pathToFileURL(`${server_root}/server/manifest.js`).href);
|
|
447
465
|
|
|
448
466
|
override({
|
|
449
467
|
paths: config.kit.paths,
|
|
@@ -451,11 +469,9 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
451
469
|
read: (file) => readFileSync(join(config.kit.files.assets, file))
|
|
452
470
|
});
|
|
453
471
|
|
|
454
|
-
const { manifest } = await import(pathToFileURL(`${server_root}/server/manifest.js`).href);
|
|
455
|
-
|
|
456
472
|
const app = new App(manifest);
|
|
457
473
|
|
|
458
|
-
const error =
|
|
474
|
+
const error = normalise_error_handler(log, config.kit.prerender.onError);
|
|
459
475
|
|
|
460
476
|
const files = new Set([
|
|
461
477
|
...build_data.static,
|
|
@@ -463,28 +479,12 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
463
479
|
...build_data.client.assets.map((chunk) => `${config.kit.appDir}/${chunk.fileName}`)
|
|
464
480
|
]);
|
|
465
481
|
|
|
466
|
-
/** @type {string[]} */
|
|
467
|
-
const paths = [];
|
|
468
|
-
|
|
469
482
|
build_data.static.forEach((file) => {
|
|
470
483
|
if (file.endsWith('/index.html')) {
|
|
471
484
|
files.add(file.slice(0, -11));
|
|
472
485
|
}
|
|
473
486
|
});
|
|
474
487
|
|
|
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
488
|
const q = queue(config.kit.prerender.concurrency);
|
|
489
489
|
|
|
490
490
|
/**
|
|
@@ -492,148 +492,168 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
492
492
|
* @param {boolean} is_html
|
|
493
493
|
*/
|
|
494
494
|
function output_filename(path, is_html) {
|
|
495
|
-
|
|
496
|
-
|
|
495
|
+
const file = path.slice(config.kit.paths.base.length + 1);
|
|
496
|
+
|
|
497
|
+
if (file === '') {
|
|
498
|
+
return 'index.html';
|
|
497
499
|
}
|
|
498
|
-
|
|
499
|
-
if (is_html &&
|
|
500
|
-
|
|
501
|
-
parts.push('index.html');
|
|
502
|
-
} else {
|
|
503
|
-
parts[parts.length - 1] += '.html';
|
|
504
|
-
}
|
|
500
|
+
|
|
501
|
+
if (is_html && !file.endsWith('.html')) {
|
|
502
|
+
return file + (config.kit.trailingSlash === 'always' ? 'index.html' : '.html');
|
|
505
503
|
}
|
|
506
|
-
|
|
504
|
+
|
|
505
|
+
return file;
|
|
507
506
|
}
|
|
508
507
|
|
|
508
|
+
const seen = new Set();
|
|
509
|
+
const written = new Set();
|
|
510
|
+
|
|
509
511
|
/**
|
|
510
|
-
* @param {string}
|
|
511
|
-
* @param {string
|
|
512
|
+
* @param {string | null} referrer
|
|
513
|
+
* @param {string} decoded
|
|
514
|
+
* @param {string} [encoded]
|
|
512
515
|
*/
|
|
513
|
-
function enqueue(
|
|
514
|
-
|
|
516
|
+
function enqueue(referrer, decoded, encoded) {
|
|
517
|
+
if (seen.has(decoded)) return;
|
|
518
|
+
seen.add(decoded);
|
|
515
519
|
|
|
516
|
-
|
|
517
|
-
|
|
520
|
+
const file = decoded.slice(config.kit.paths.base.length + 1);
|
|
521
|
+
if (files.has(file)) return;
|
|
518
522
|
|
|
519
|
-
return q.add(() => visit(
|
|
523
|
+
return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer));
|
|
520
524
|
}
|
|
521
525
|
|
|
522
526
|
/**
|
|
523
|
-
* @param {string}
|
|
524
|
-
* @param {string}
|
|
527
|
+
* @param {string} decoded
|
|
528
|
+
* @param {string} encoded
|
|
525
529
|
* @param {string?} referrer
|
|
526
530
|
*/
|
|
527
|
-
async function visit(
|
|
531
|
+
async function visit(decoded, encoded, referrer) {
|
|
532
|
+
if (!decoded.startsWith(config.kit.paths.base)) {
|
|
533
|
+
error({ status: 404, path: decoded, referrer, referenceType: 'linked' });
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
|
|
528
537
|
/** @type {Map<string, import('types/internal').PrerenderDependency>} */
|
|
529
538
|
const dependencies = new Map();
|
|
530
539
|
|
|
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), {
|
|
540
|
+
const response = await app.render(new Request(`http://sveltekit-prerender${encoded}`), {
|
|
536
541
|
prerender: {
|
|
537
542
|
all,
|
|
538
543
|
dependencies
|
|
539
544
|
}
|
|
540
545
|
});
|
|
541
546
|
|
|
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';
|
|
547
|
+
const text = await response.text();
|
|
546
548
|
|
|
547
|
-
|
|
549
|
+
save(response, text, decoded, encoded, referrer, 'linked');
|
|
548
550
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
+
for (const [dependency_path, result] of dependencies) {
|
|
552
|
+
// this seems circuitous, but using new URL allows us to not care
|
|
553
|
+
// whether dependency_path is encoded or not
|
|
554
|
+
const encoded_dependency_path = new URL(dependency_path, 'http://localhost').pathname;
|
|
555
|
+
const decoded_dependency_path = decodeURI(encoded_dependency_path);
|
|
551
556
|
|
|
552
|
-
|
|
553
|
-
|
|
557
|
+
const body = result.body ?? new Uint8Array(await result.response.arrayBuffer());
|
|
558
|
+
save(
|
|
559
|
+
result.response,
|
|
560
|
+
body,
|
|
561
|
+
decoded_dependency_path,
|
|
562
|
+
encoded_dependency_path,
|
|
563
|
+
decoded,
|
|
564
|
+
'fetched'
|
|
565
|
+
);
|
|
566
|
+
}
|
|
554
567
|
|
|
555
|
-
|
|
568
|
+
if (config.kit.prerender.crawl && response.headers.get('content-type') === 'text/html') {
|
|
569
|
+
for (const href of crawl(text)) {
|
|
570
|
+
if (href.startsWith('data:') || href.startsWith('#')) continue;
|
|
556
571
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
`<meta http-equiv="refresh" content=${escape_html_attr(`0;url=${location}`)}>`
|
|
560
|
-
);
|
|
572
|
+
const resolved = resolve(encoded, href);
|
|
573
|
+
if (!is_root_relative(resolved)) continue;
|
|
561
574
|
|
|
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
|
-
}
|
|
575
|
+
const parsed = new URL(resolved, 'http://localhost');
|
|
569
576
|
|
|
570
|
-
|
|
571
|
-
}
|
|
577
|
+
if (parsed.search) ;
|
|
572
578
|
|
|
573
|
-
|
|
579
|
+
const pathname = normalize_path(parsed.pathname, config.kit.trailingSlash);
|
|
580
|
+
enqueue(decoded, decodeURI(pathname), pathname);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
574
584
|
|
|
575
|
-
|
|
576
|
-
|
|
585
|
+
/**
|
|
586
|
+
* @param {Response} response
|
|
587
|
+
* @param {string | Uint8Array} body
|
|
588
|
+
* @param {string} decoded
|
|
589
|
+
* @param {string} encoded
|
|
590
|
+
* @param {string | null} referrer
|
|
591
|
+
* @param {'linked' | 'fetched'} referenceType
|
|
592
|
+
*/
|
|
593
|
+
function save(response, body, decoded, encoded, referrer, referenceType) {
|
|
594
|
+
const response_type = Math.floor(response.status / 100);
|
|
595
|
+
const type = /** @type {string} */ (response.headers.get('content-type'));
|
|
596
|
+
const is_html = response_type === REDIRECT || type === 'text/html';
|
|
577
597
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
paths.push(normalize(decoded_path));
|
|
581
|
-
} else if (response_type !== OK) {
|
|
582
|
-
error({ status: rendered.status, path, referrer, referenceType: 'linked' });
|
|
583
|
-
}
|
|
598
|
+
const file = output_filename(decoded, is_html);
|
|
599
|
+
const dest = `${out}/${file}`;
|
|
584
600
|
|
|
585
|
-
|
|
586
|
-
|
|
601
|
+
if (written.has(file)) return;
|
|
602
|
+
written.add(file);
|
|
587
603
|
|
|
588
|
-
|
|
604
|
+
if (response_type === REDIRECT) {
|
|
605
|
+
const location = response.headers.get('location');
|
|
589
606
|
|
|
590
|
-
|
|
607
|
+
if (location) {
|
|
608
|
+
mkdirp(dirname(dest));
|
|
591
609
|
|
|
592
|
-
|
|
593
|
-
mkdirp(dirname(file));
|
|
610
|
+
log.warn(`${response.status} ${decoded} -> ${location}`);
|
|
594
611
|
|
|
595
612
|
writeFileSync(
|
|
596
|
-
|
|
597
|
-
|
|
613
|
+
dest,
|
|
614
|
+
`<meta http-equiv="refresh" content=${escape_html_attr(`0;url=${location}`)}>`
|
|
598
615
|
);
|
|
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
|
-
|
|
613
|
-
if (is_html && config.kit.prerender.crawl) {
|
|
614
|
-
for (const href of crawl(text)) {
|
|
615
|
-
if (href.startsWith('data:') || href.startsWith('#')) continue;
|
|
616
616
|
|
|
617
|
-
|
|
618
|
-
|
|
617
|
+
let resolved = resolve(encoded, location);
|
|
618
|
+
if (is_root_relative(resolved)) {
|
|
619
|
+
resolved = normalize_path(resolved, config.kit.trailingSlash);
|
|
620
|
+
enqueue(decoded, decodeURI(resolved), resolved);
|
|
621
|
+
}
|
|
619
622
|
|
|
620
|
-
|
|
623
|
+
if (!prerendered.redirects.has(decoded)) {
|
|
624
|
+
prerendered.redirects.set(decoded, {
|
|
625
|
+
status: response.status,
|
|
626
|
+
location: resolved
|
|
627
|
+
});
|
|
621
628
|
|
|
622
|
-
|
|
629
|
+
prerendered.paths.push(normalize_path(decoded, 'never'));
|
|
630
|
+
}
|
|
631
|
+
} else {
|
|
632
|
+
log.warn(`location header missing on redirect received from ${decoded}`);
|
|
633
|
+
}
|
|
623
634
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
pathname = pathname.slice(config.kit.paths.base.length) || '/';
|
|
627
|
-
}
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
628
637
|
|
|
629
|
-
|
|
630
|
-
|
|
638
|
+
if (response.status === 200) {
|
|
639
|
+
mkdirp(dirname(dest));
|
|
631
640
|
|
|
632
|
-
|
|
641
|
+
log.info(`${response.status} ${decoded}`);
|
|
642
|
+
writeFileSync(dest, body);
|
|
633
643
|
|
|
634
|
-
|
|
635
|
-
|
|
644
|
+
if (is_html) {
|
|
645
|
+
prerendered.pages.set(decoded, {
|
|
646
|
+
file
|
|
647
|
+
});
|
|
648
|
+
} else {
|
|
649
|
+
prerendered.assets.set(decoded, {
|
|
650
|
+
type
|
|
651
|
+
});
|
|
636
652
|
}
|
|
653
|
+
|
|
654
|
+
prerendered.paths.push(normalize_path(decoded, 'never'));
|
|
655
|
+
} else if (response_type !== OK) {
|
|
656
|
+
error({ status: response.status, path: decoded, referrer, referenceType });
|
|
637
657
|
}
|
|
638
658
|
}
|
|
639
659
|
|
|
@@ -641,10 +661,10 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
641
661
|
for (const entry of config.kit.prerender.entries) {
|
|
642
662
|
if (entry === '*') {
|
|
643
663
|
for (const entry of build_data.entries) {
|
|
644
|
-
enqueue(entry,
|
|
664
|
+
enqueue(null, normalize_path(config.kit.paths.base + entry, config.kit.trailingSlash)); // TODO can we pre-normalize these?
|
|
645
665
|
}
|
|
646
666
|
} else {
|
|
647
|
-
enqueue(entry,
|
|
667
|
+
enqueue(null, normalize_path(config.kit.paths.base + entry, config.kit.trailingSlash));
|
|
648
668
|
}
|
|
649
669
|
}
|
|
650
670
|
|
|
@@ -665,9 +685,7 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
665
685
|
writeFileSync(file, await rendered.text());
|
|
666
686
|
}
|
|
667
687
|
|
|
668
|
-
return
|
|
669
|
-
paths
|
|
670
|
-
};
|
|
688
|
+
return prerendered;
|
|
671
689
|
}
|
|
672
690
|
|
|
673
691
|
/**
|
|
@@ -681,11 +699,14 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
|
|
|
681
699
|
*/
|
|
682
700
|
function create_builder({ cwd, config, build_data, log }) {
|
|
683
701
|
/** @type {Set<string>} */
|
|
684
|
-
|
|
702
|
+
let prerendered_paths;
|
|
703
|
+
|
|
685
704
|
let generated_manifest = false;
|
|
686
705
|
|
|
687
706
|
/** @param {import('types/internal').RouteData} route */
|
|
688
707
|
function not_prerendered(route) {
|
|
708
|
+
if (!prerendered_paths) return true;
|
|
709
|
+
|
|
689
710
|
if (route.type === 'page' && route.path) {
|
|
690
711
|
return !prerendered_paths.has(route.path);
|
|
691
712
|
}
|
|
@@ -700,6 +721,7 @@ function create_builder({ cwd, config, build_data, log }) {
|
|
|
700
721
|
copy,
|
|
701
722
|
|
|
702
723
|
appDir: config.kit.appDir,
|
|
724
|
+
trailingSlash: config.kit.trailingSlash,
|
|
703
725
|
|
|
704
726
|
createEntries(fn) {
|
|
705
727
|
generated_manifest = true;
|
|
@@ -829,10 +851,7 @@ function create_builder({ cwd, config, build_data, log }) {
|
|
|
829
851
|
log
|
|
830
852
|
});
|
|
831
853
|
|
|
832
|
-
prerendered.paths
|
|
833
|
-
prerendered_paths.add(path);
|
|
834
|
-
prerendered_paths.add(path + '/');
|
|
835
|
-
});
|
|
854
|
+
prerendered_paths = new Set(prerendered.paths);
|
|
836
855
|
|
|
837
856
|
return prerendered;
|
|
838
857
|
}
|
package/dist/chunks/index7.js
CHANGED
|
@@ -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
|
|
15300
|
-
const
|
|
15301
|
-
|
|
15302
|
-
fs.
|
|
15303
|
-
|
|
15304
|
-
|
|
15305
|
-
|
|
15306
|
-
fs.
|
|
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
|
|
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
|
|
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
|
|
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#
|
|
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#
|
|
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#
|
|
610
|
+
`${keypath} option must not end with '/'. See https://kit.svelte.dev/docs/configuration#paths`
|
|
611
611
|
);
|
|
612
612
|
}
|
|
613
613
|
}
|
|
@@ -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,13 +678,13 @@ 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
|
|
682
685
|
ssr: error(
|
|
683
686
|
(keypath) =>
|
|
684
|
-
`${keypath} has been removed — use the handle hook instead: https://kit.svelte.dev/docs#
|
|
687
|
+
`${keypath} has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle'`
|
|
685
688
|
),
|
|
686
689
|
|
|
687
690
|
// TODO remove this for 1.0
|
|
@@ -904,7 +907,7 @@ async function load_config({ cwd = process.cwd() } = {}) {
|
|
|
904
907
|
|
|
905
908
|
if (!fs__default.existsSync(config_file)) {
|
|
906
909
|
throw new Error(
|
|
907
|
-
'You need to create a svelte.config.js file. See https://kit.svelte.dev/docs
|
|
910
|
+
'You need to create a svelte.config.js file. See https://kit.svelte.dev/docs/configuration'
|
|
908
911
|
);
|
|
909
912
|
}
|
|
910
913
|
|
|
@@ -929,7 +932,7 @@ async function load_config({ cwd = process.cwd() } = {}) {
|
|
|
929
932
|
function validate_config(config) {
|
|
930
933
|
if (typeof config !== 'object') {
|
|
931
934
|
throw new Error(
|
|
932
|
-
'svelte.config.js must have a configuration object as its default export. See https://kit.svelte.dev/docs
|
|
935
|
+
'svelte.config.js must have a configuration object as its default export. See https://kit.svelte.dev/docs/configuration'
|
|
933
936
|
);
|
|
934
937
|
}
|
|
935
938
|
|
|
@@ -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.267');
|
|
999
1002
|
|
|
1000
1003
|
prog
|
|
1001
1004
|
.command('dev')
|
|
@@ -1066,7 +1069,7 @@ prog
|
|
|
1066
1069
|
|
|
1067
1070
|
// prettier-ignore
|
|
1068
1071
|
console.log(
|
|
1069
|
-
`See ${$.bold().cyan('https://kit.svelte.dev/docs
|
|
1072
|
+
`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
1073
|
);
|
|
1071
1074
|
} catch (error) {
|
|
1072
1075
|
handle_error(error);
|
|
@@ -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.267'}\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.267",
|
|
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';
|