@sveltejs/kit 1.0.0-next.258 → 1.0.0-next.261
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 +74 -14
- package/assets/server/index.js +333 -46
- package/dist/chunks/index.js +6 -0
- package/dist/chunks/index2.js +33 -3
- package/dist/chunks/index3.js +8 -22
- package/dist/chunks/index4.js +1 -0
- package/dist/cli.js +2 -2
- package/package.json +1 -1
- package/types/endpoint.d.ts +23 -2
- package/types/helper.d.ts +2 -8
- package/types/internal.d.ts +11 -2
- package/types/page.d.ts +1 -0
package/assets/client/start.js
CHANGED
|
@@ -189,8 +189,11 @@ class Router {
|
|
|
189
189
|
// Ignore if <a> has a target
|
|
190
190
|
if (a instanceof SVGAElement ? a.target.baseVal : a.target) return;
|
|
191
191
|
|
|
192
|
-
// Check if new url only differs by hash
|
|
193
|
-
|
|
192
|
+
// Check if new url only differs by hash and use the browser default behavior in that case
|
|
193
|
+
// This will ensure the `hashchange` event is fired
|
|
194
|
+
// Removing the hash does a full page navigation in the browser, so make sure a hash is present
|
|
195
|
+
const [base, hash] = url.href.split('#');
|
|
196
|
+
if (hash !== undefined && base === location.href.split('#')[0]) {
|
|
194
197
|
// Call `pushState` to add url to history so going back works.
|
|
195
198
|
// Also make a delay, otherwise the browser default behaviour would not kick in
|
|
196
199
|
setTimeout(() => history.pushState({}, '', url.href));
|
|
@@ -731,15 +734,29 @@ class Renderer {
|
|
|
731
734
|
for (let i = 0; i < nodes.length; i += 1) {
|
|
732
735
|
const is_leaf = i === nodes.length - 1;
|
|
733
736
|
|
|
737
|
+
let props;
|
|
738
|
+
|
|
739
|
+
if (is_leaf) {
|
|
740
|
+
const serialized = document.querySelector('[data-type="svelte-props"]');
|
|
741
|
+
if (serialized) {
|
|
742
|
+
props = JSON.parse(/** @type {string} */ (serialized.textContent));
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
734
746
|
const node = await this._load_node({
|
|
735
747
|
module: await nodes[i],
|
|
736
748
|
url,
|
|
737
749
|
params,
|
|
738
750
|
stuff,
|
|
739
751
|
status: is_leaf ? status : undefined,
|
|
740
|
-
error: is_leaf ? error : undefined
|
|
752
|
+
error: is_leaf ? error : undefined,
|
|
753
|
+
props
|
|
741
754
|
});
|
|
742
755
|
|
|
756
|
+
if (props) {
|
|
757
|
+
node.uses.dependencies.add(url.href);
|
|
758
|
+
}
|
|
759
|
+
|
|
743
760
|
branch.push(node);
|
|
744
761
|
|
|
745
762
|
if (node && node.loaded) {
|
|
@@ -1092,10 +1109,11 @@ class Renderer {
|
|
|
1092
1109
|
* url: URL;
|
|
1093
1110
|
* params: Record<string, string>;
|
|
1094
1111
|
* stuff: Record<string, any>;
|
|
1112
|
+
* props?: Record<string, any>;
|
|
1095
1113
|
* }} options
|
|
1096
1114
|
* @returns
|
|
1097
1115
|
*/
|
|
1098
|
-
async _load_node({ status, error, module, url, params, stuff }) {
|
|
1116
|
+
async _load_node({ status, error, module, url, params, stuff, props }) {
|
|
1099
1117
|
/** @type {import('./types').BranchNode} */
|
|
1100
1118
|
const node = {
|
|
1101
1119
|
module,
|
|
@@ -1104,12 +1122,17 @@ class Renderer {
|
|
|
1104
1122
|
url: false,
|
|
1105
1123
|
session: false,
|
|
1106
1124
|
stuff: false,
|
|
1107
|
-
dependencies:
|
|
1125
|
+
dependencies: new Set()
|
|
1108
1126
|
},
|
|
1109
1127
|
loaded: null,
|
|
1110
1128
|
stuff
|
|
1111
1129
|
};
|
|
1112
1130
|
|
|
1131
|
+
if (props) {
|
|
1132
|
+
// shadow endpoint props means we need to mark this URL as a dependency of itself
|
|
1133
|
+
node.uses.dependencies.add(url.href);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1113
1136
|
/** @type {Record<string, string>} */
|
|
1114
1137
|
const uses_params = {};
|
|
1115
1138
|
for (const key in params) {
|
|
@@ -1130,6 +1153,7 @@ class Renderer {
|
|
|
1130
1153
|
/** @type {import('types/page').LoadInput | import('types/page').ErrorLoadInput} */
|
|
1131
1154
|
const load_input = {
|
|
1132
1155
|
params: uses_params,
|
|
1156
|
+
props: props || {},
|
|
1133
1157
|
get url() {
|
|
1134
1158
|
node.uses.url = true;
|
|
1135
1159
|
return url;
|
|
@@ -1145,7 +1169,7 @@ class Renderer {
|
|
|
1145
1169
|
fetch(resource, info) {
|
|
1146
1170
|
const requested = typeof resource === 'string' ? resource : resource.url;
|
|
1147
1171
|
const { href } = new URL(requested, url);
|
|
1148
|
-
node.uses.dependencies.
|
|
1172
|
+
node.uses.dependencies.add(href);
|
|
1149
1173
|
|
|
1150
1174
|
return started ? fetch(resource, info) : initial_fetch(resource, info);
|
|
1151
1175
|
}
|
|
@@ -1173,6 +1197,8 @@ class Renderer {
|
|
|
1173
1197
|
|
|
1174
1198
|
node.loaded = normalize(loaded);
|
|
1175
1199
|
if (node.loaded.stuff) node.stuff = node.loaded.stuff;
|
|
1200
|
+
} else if (props) {
|
|
1201
|
+
node.loaded = normalize({ props });
|
|
1176
1202
|
}
|
|
1177
1203
|
|
|
1178
1204
|
return node;
|
|
@@ -1191,7 +1217,7 @@ class Renderer {
|
|
|
1191
1217
|
if (cached) return cached;
|
|
1192
1218
|
}
|
|
1193
1219
|
|
|
1194
|
-
const [pattern, a, b, get_params] = route;
|
|
1220
|
+
const [pattern, a, b, get_params, has_shadow] = route;
|
|
1195
1221
|
const params = get_params
|
|
1196
1222
|
? // the pattern is for the route which we've already matched to this path
|
|
1197
1223
|
get_params(/** @type {RegExpExecArray} */ (pattern.exec(path)))
|
|
@@ -1235,16 +1261,50 @@ class Renderer {
|
|
|
1235
1261
|
(changed.url && previous.uses.url) ||
|
|
1236
1262
|
changed.params.some((param) => previous.uses.params.has(param)) ||
|
|
1237
1263
|
(changed.session && previous.uses.session) ||
|
|
1238
|
-
previous.uses.dependencies.some((dep) => this.invalid.has(dep)) ||
|
|
1264
|
+
Array.from(previous.uses.dependencies).some((dep) => this.invalid.has(dep)) ||
|
|
1239
1265
|
(stuff_changed && previous.uses.stuff);
|
|
1240
1266
|
|
|
1241
1267
|
if (changed_since_last_render) {
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1268
|
+
/** @type {Record<string, any>} */
|
|
1269
|
+
let props = {};
|
|
1270
|
+
|
|
1271
|
+
if (has_shadow && i === a.length - 1) {
|
|
1272
|
+
const res = await fetch(
|
|
1273
|
+
`${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}__data.json`,
|
|
1274
|
+
{
|
|
1275
|
+
headers: {
|
|
1276
|
+
'x-sveltekit-noredirect': 'true'
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
);
|
|
1280
|
+
|
|
1281
|
+
if (res.ok) {
|
|
1282
|
+
const redirect = res.headers.get('x-sveltekit-location');
|
|
1283
|
+
|
|
1284
|
+
if (redirect) {
|
|
1285
|
+
return {
|
|
1286
|
+
redirect,
|
|
1287
|
+
props: {},
|
|
1288
|
+
state: this.current
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
props = await res.json();
|
|
1293
|
+
} else {
|
|
1294
|
+
status = res.status;
|
|
1295
|
+
error = new Error('Failed to load data');
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
if (!error) {
|
|
1300
|
+
node = await this._load_node({
|
|
1301
|
+
module,
|
|
1302
|
+
url,
|
|
1303
|
+
params,
|
|
1304
|
+
props,
|
|
1305
|
+
stuff
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1248
1308
|
|
|
1249
1309
|
if (node && node.loaded) {
|
|
1250
1310
|
if (node.loaded.fallthrough) {
|
package/assets/server/index.js
CHANGED
|
@@ -61,6 +61,24 @@ function decode_params(params) {
|
|
|
61
61
|
return params;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/** @param {any} body */
|
|
65
|
+
function is_pojo(body) {
|
|
66
|
+
if (typeof body !== 'object') return false;
|
|
67
|
+
|
|
68
|
+
if (body) {
|
|
69
|
+
if (body instanceof Uint8Array) return false;
|
|
70
|
+
|
|
71
|
+
// body could be a node Readable, but we don't want to import
|
|
72
|
+
// node built-ins, so we use duck typing
|
|
73
|
+
if (body._readableState && body._writableState && body._events) return false;
|
|
74
|
+
|
|
75
|
+
// similarly, it could be a web ReadableStream
|
|
76
|
+
if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
64
82
|
/** @param {string} body */
|
|
65
83
|
function error(body) {
|
|
66
84
|
return new Response(body, {
|
|
@@ -95,13 +113,10 @@ function is_text(content_type) {
|
|
|
95
113
|
|
|
96
114
|
/**
|
|
97
115
|
* @param {import('types/hooks').RequestEvent} event
|
|
98
|
-
* @param {import('types/
|
|
99
|
-
* @param {RegExpExecArray} match
|
|
116
|
+
* @param {{ [method: string]: import('types/endpoint').RequestHandler }} mod
|
|
100
117
|
* @returns {Promise<Response | undefined>}
|
|
101
118
|
*/
|
|
102
|
-
async function render_endpoint(event,
|
|
103
|
-
const mod = await route.load();
|
|
104
|
-
|
|
119
|
+
async function render_endpoint(event, mod) {
|
|
105
120
|
/** @type {import('types/endpoint').RequestHandler} */
|
|
106
121
|
const handler = mod[event.request.method.toLowerCase().replace('delete', 'del')]; // 'delete' is a reserved word
|
|
107
122
|
|
|
@@ -109,11 +124,6 @@ async function render_endpoint(event, route, match) {
|
|
|
109
124
|
return;
|
|
110
125
|
}
|
|
111
126
|
|
|
112
|
-
// we're mutating `request` so that we don't have to do { ...request, params }
|
|
113
|
-
// on the next line, since that breaks the getters that replace path, query and
|
|
114
|
-
// origin. We could revert that once we remove the getters
|
|
115
|
-
event.params = route.params ? decode_params(route.params(match)) : {};
|
|
116
|
-
|
|
117
127
|
const response = await handler(event);
|
|
118
128
|
const preface = `Invalid response from route ${event.url.pathname}`;
|
|
119
129
|
|
|
@@ -163,24 +173,6 @@ async function render_endpoint(event, route, match) {
|
|
|
163
173
|
});
|
|
164
174
|
}
|
|
165
175
|
|
|
166
|
-
/** @param {any} body */
|
|
167
|
-
function is_pojo(body) {
|
|
168
|
-
if (typeof body !== 'object') return false;
|
|
169
|
-
|
|
170
|
-
if (body) {
|
|
171
|
-
if (body instanceof Uint8Array) return false;
|
|
172
|
-
|
|
173
|
-
// body could be a node Readable, but we don't want to import
|
|
174
|
-
// node built-ins, so we use duck typing
|
|
175
|
-
if (body._readableState && body._writableState && body._events) return false;
|
|
176
|
-
|
|
177
|
-
// similarly, it could be a web ReadableStream
|
|
178
|
-
if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) return false;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return true;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
176
|
var chars$1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$';
|
|
185
177
|
var unsafeChars = /[<>\b\f\n\r\t\0\u2028\u2029]/g;
|
|
186
178
|
var reserved = /^(?:do|if|in|for|int|let|new|try|var|byte|case|char|else|enum|goto|long|this|void|with|await|break|catch|class|const|final|float|short|super|throw|while|yield|delete|double|export|import|native|return|switch|throws|typeof|boolean|default|extends|finally|package|private|abstract|continue|debugger|function|volatile|interface|protected|transient|implements|instanceof|synchronized)$/;
|
|
@@ -482,8 +474,7 @@ function coalesce_to_error(err) {
|
|
|
482
474
|
}
|
|
483
475
|
|
|
484
476
|
/** @type {Record<string, string>} */
|
|
485
|
-
const
|
|
486
|
-
'"': '\\"',
|
|
477
|
+
const escape_json_in_html_dict = {
|
|
487
478
|
'<': '\\u003C',
|
|
488
479
|
'>': '\\u003E',
|
|
489
480
|
'/': '\\u002F',
|
|
@@ -498,7 +489,24 @@ const escape_json_string_in_html_dict = {
|
|
|
498
489
|
'\u2029': '\\u2029'
|
|
499
490
|
};
|
|
500
491
|
|
|
501
|
-
/** @
|
|
492
|
+
/** @type {Record<string, string>} */
|
|
493
|
+
const escape_json_string_in_html_dict = {
|
|
494
|
+
'"': '\\"',
|
|
495
|
+
...escape_json_in_html_dict
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Escape a stringified JSON object that's going to be embedded in a `<script>` tag
|
|
500
|
+
* @param {string} str
|
|
501
|
+
*/
|
|
502
|
+
function escape_json_in_html(str) {
|
|
503
|
+
return escape(str, escape_json_in_html_dict, (code) => `\\u${code.toString(16).toUpperCase()}`);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Escape a string JSON value to be embedded into a `<script>` tag
|
|
508
|
+
* @param {string} str
|
|
509
|
+
*/
|
|
502
510
|
function escape_json_string_in_html(str) {
|
|
503
511
|
return escape(
|
|
504
512
|
str,
|
|
@@ -1076,6 +1084,8 @@ async function render_response({
|
|
|
1076
1084
|
/** @type {Array<{ url: string, body: string, json: string }>} */
|
|
1077
1085
|
const serialized_data = [];
|
|
1078
1086
|
|
|
1087
|
+
let shadow_props;
|
|
1088
|
+
|
|
1079
1089
|
let rendered;
|
|
1080
1090
|
|
|
1081
1091
|
let is_private = false;
|
|
@@ -1086,13 +1096,14 @@ async function render_response({
|
|
|
1086
1096
|
}
|
|
1087
1097
|
|
|
1088
1098
|
if (ssr) {
|
|
1089
|
-
branch.forEach(({ node, loaded, fetched, uses_credentials }) => {
|
|
1099
|
+
branch.forEach(({ node, props, loaded, fetched, uses_credentials }) => {
|
|
1090
1100
|
if (node.css) node.css.forEach((url) => stylesheets.add(url));
|
|
1091
1101
|
if (node.js) node.js.forEach((url) => modulepreloads.add(url));
|
|
1092
1102
|
if (node.styles) Object.entries(node.styles).forEach(([k, v]) => styles.set(k, v));
|
|
1093
1103
|
|
|
1094
1104
|
// TODO probably better if `fetched` wasn't populated unless `hydrate`
|
|
1095
1105
|
if (fetched && page_config.hydrate) serialized_data.push(...fetched);
|
|
1106
|
+
if (props) shadow_props = props;
|
|
1096
1107
|
|
|
1097
1108
|
if (uses_credentials) is_private = true;
|
|
1098
1109
|
|
|
@@ -1271,6 +1282,11 @@ async function render_response({
|
|
|
1271
1282
|
return `<script ${attributes}>${json}</script>`;
|
|
1272
1283
|
})
|
|
1273
1284
|
.join('\n\t');
|
|
1285
|
+
|
|
1286
|
+
if (shadow_props) {
|
|
1287
|
+
// prettier-ignore
|
|
1288
|
+
body += `<script type="application/json" data-type="svelte-props">${escape_json_in_html(s(shadow_props))}</script>`;
|
|
1289
|
+
}
|
|
1274
1290
|
}
|
|
1275
1291
|
|
|
1276
1292
|
if (options.service_worker) {
|
|
@@ -1476,6 +1492,7 @@ function is_root_relative(path) {
|
|
|
1476
1492
|
* $session: any;
|
|
1477
1493
|
* stuff: Record<string, any>;
|
|
1478
1494
|
* is_error: boolean;
|
|
1495
|
+
* is_leaf: boolean;
|
|
1479
1496
|
* status?: number;
|
|
1480
1497
|
* error?: Error;
|
|
1481
1498
|
* }} opts
|
|
@@ -1492,6 +1509,7 @@ async function load_node({
|
|
|
1492
1509
|
$session,
|
|
1493
1510
|
stuff,
|
|
1494
1511
|
is_error,
|
|
1512
|
+
is_leaf,
|
|
1495
1513
|
status,
|
|
1496
1514
|
error
|
|
1497
1515
|
}) {
|
|
@@ -1513,13 +1531,40 @@ async function load_node({
|
|
|
1513
1531
|
*/
|
|
1514
1532
|
let set_cookie_headers = [];
|
|
1515
1533
|
|
|
1534
|
+
/** @type {import('types/helper').Either<import('types/endpoint').Fallthrough, import('types/page').LoadOutput>} */
|
|
1516
1535
|
let loaded;
|
|
1517
1536
|
|
|
1518
|
-
|
|
1537
|
+
/** @type {import('types/endpoint').ShadowData} */
|
|
1538
|
+
const shadow = is_leaf
|
|
1539
|
+
? await load_shadow_data(
|
|
1540
|
+
/** @type {import('types/internal').SSRPage} */ (route),
|
|
1541
|
+
event,
|
|
1542
|
+
!!state.prerender
|
|
1543
|
+
)
|
|
1544
|
+
: {};
|
|
1545
|
+
|
|
1546
|
+
if (shadow.fallthrough) return;
|
|
1547
|
+
|
|
1548
|
+
if (shadow.cookies) {
|
|
1549
|
+
set_cookie_headers.push(...shadow.cookies);
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
if (shadow.error) {
|
|
1553
|
+
loaded = {
|
|
1554
|
+
status: shadow.status,
|
|
1555
|
+
error: shadow.error
|
|
1556
|
+
};
|
|
1557
|
+
} else if (shadow.redirect) {
|
|
1558
|
+
loaded = {
|
|
1559
|
+
status: shadow.status,
|
|
1560
|
+
redirect: shadow.redirect
|
|
1561
|
+
};
|
|
1562
|
+
} else if (module.load) {
|
|
1519
1563
|
/** @type {import('types/page').LoadInput | import('types/page').ErrorLoadInput} */
|
|
1520
1564
|
const load_input = {
|
|
1521
1565
|
url: state.prerender ? create_prerendering_url_proxy(url) : url,
|
|
1522
1566
|
params,
|
|
1567
|
+
props: shadow.body || {},
|
|
1523
1568
|
get session() {
|
|
1524
1569
|
uses_credentials = true;
|
|
1525
1570
|
return $session;
|
|
@@ -1555,9 +1600,15 @@ async function load_node({
|
|
|
1555
1600
|
|
|
1556
1601
|
// merge headers from request
|
|
1557
1602
|
for (const [key, value] of event.request.headers) {
|
|
1558
|
-
if (
|
|
1559
|
-
|
|
1560
|
-
|
|
1603
|
+
if (
|
|
1604
|
+
key !== 'authorization' &&
|
|
1605
|
+
key !== 'cookie' &&
|
|
1606
|
+
key !== 'host' &&
|
|
1607
|
+
key !== 'if-none-match' &&
|
|
1608
|
+
!opts.headers.has(key)
|
|
1609
|
+
) {
|
|
1610
|
+
opts.headers.set(key, value);
|
|
1611
|
+
}
|
|
1561
1612
|
}
|
|
1562
1613
|
|
|
1563
1614
|
opts.headers.set('referer', event.url.href);
|
|
@@ -1745,6 +1796,10 @@ async function load_node({
|
|
|
1745
1796
|
if (!loaded) {
|
|
1746
1797
|
throw new Error(`load function must return a value${options.dev ? ` (${node.entry})` : ''}`);
|
|
1747
1798
|
}
|
|
1799
|
+
} else if (shadow.body) {
|
|
1800
|
+
loaded = {
|
|
1801
|
+
props: shadow.body
|
|
1802
|
+
};
|
|
1748
1803
|
} else {
|
|
1749
1804
|
loaded = {};
|
|
1750
1805
|
}
|
|
@@ -1753,8 +1808,21 @@ async function load_node({
|
|
|
1753
1808
|
return;
|
|
1754
1809
|
}
|
|
1755
1810
|
|
|
1811
|
+
// generate __data.json files when prerendering
|
|
1812
|
+
if (shadow.body && state.prerender) {
|
|
1813
|
+
const pathname = `${event.url.pathname}/__data.json`;
|
|
1814
|
+
|
|
1815
|
+
const dependency = {
|
|
1816
|
+
response: new Response(undefined),
|
|
1817
|
+
body: JSON.stringify(shadow.body)
|
|
1818
|
+
};
|
|
1819
|
+
|
|
1820
|
+
state.prerender.dependencies.set(pathname, dependency);
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1756
1823
|
return {
|
|
1757
1824
|
node,
|
|
1825
|
+
props: shadow.body,
|
|
1758
1826
|
loaded: normalize(loaded),
|
|
1759
1827
|
stuff: loaded.stuff || stuff,
|
|
1760
1828
|
fetched,
|
|
@@ -1763,6 +1831,127 @@ async function load_node({
|
|
|
1763
1831
|
};
|
|
1764
1832
|
}
|
|
1765
1833
|
|
|
1834
|
+
/**
|
|
1835
|
+
*
|
|
1836
|
+
* @param {import('types/internal').SSRPage} route
|
|
1837
|
+
* @param {import('types/hooks').RequestEvent} event
|
|
1838
|
+
* @param {boolean} prerender
|
|
1839
|
+
* @returns {Promise<import('types/endpoint').ShadowData>}
|
|
1840
|
+
*/
|
|
1841
|
+
async function load_shadow_data(route, event, prerender) {
|
|
1842
|
+
if (!route.shadow) return {};
|
|
1843
|
+
|
|
1844
|
+
try {
|
|
1845
|
+
const mod = await route.shadow();
|
|
1846
|
+
|
|
1847
|
+
if (prerender && (mod.post || mod.put || mod.del || mod.patch)) {
|
|
1848
|
+
throw new Error('Cannot prerender pages that have shadow endpoints with mutative methods');
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
const method = event.request.method.toLowerCase().replace('delete', 'del');
|
|
1852
|
+
const handler = mod[method];
|
|
1853
|
+
|
|
1854
|
+
if (!handler) {
|
|
1855
|
+
return {
|
|
1856
|
+
status: 405,
|
|
1857
|
+
error: new Error(`${method} method not allowed`)
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
/** @type {import('types/endpoint').ShadowData} */
|
|
1862
|
+
const data = {
|
|
1863
|
+
status: 200,
|
|
1864
|
+
cookies: [],
|
|
1865
|
+
body: {}
|
|
1866
|
+
};
|
|
1867
|
+
|
|
1868
|
+
if (method !== 'get') {
|
|
1869
|
+
const result = await handler(event);
|
|
1870
|
+
|
|
1871
|
+
if (result.fallthrough) return result;
|
|
1872
|
+
|
|
1873
|
+
const { status = 200, headers = {}, body = {} } = result;
|
|
1874
|
+
|
|
1875
|
+
validate_shadow_output(headers, body);
|
|
1876
|
+
|
|
1877
|
+
if (headers['set-cookie']) {
|
|
1878
|
+
/** @type {string[]} */ (data.cookies).push(...headers['set-cookie']);
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
// Redirects are respected...
|
|
1882
|
+
if (status >= 300 && status < 400) {
|
|
1883
|
+
return {
|
|
1884
|
+
status,
|
|
1885
|
+
redirect: /** @type {string} */ (
|
|
1886
|
+
headers instanceof Headers ? headers.get('location') : headers.location
|
|
1887
|
+
)
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
// ...but 4xx and 5xx status codes _don't_ result in the error page
|
|
1892
|
+
// rendering for non-GET requests — instead, we allow the page
|
|
1893
|
+
// to render with any validation errors etc that were returned
|
|
1894
|
+
data.status = status;
|
|
1895
|
+
data.body = body;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
if (mod.get) {
|
|
1899
|
+
const result = await mod.get.call(null, event);
|
|
1900
|
+
|
|
1901
|
+
if (result.fallthrough) return result;
|
|
1902
|
+
|
|
1903
|
+
const { status = 200, headers = {}, body = {} } = result;
|
|
1904
|
+
|
|
1905
|
+
validate_shadow_output(headers, body);
|
|
1906
|
+
|
|
1907
|
+
if (headers['set-cookie']) {
|
|
1908
|
+
/** @type {string[]} */ (data.cookies).push(...headers['set-cookie']);
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
if (status >= 400) {
|
|
1912
|
+
return {
|
|
1913
|
+
status,
|
|
1914
|
+
error: new Error('Failed to load data')
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
if (status >= 300) {
|
|
1919
|
+
return {
|
|
1920
|
+
status,
|
|
1921
|
+
redirect: /** @type {string} */ (
|
|
1922
|
+
headers instanceof Headers ? headers.get('location') : headers.location
|
|
1923
|
+
)
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
data.body = { ...body, ...data.body };
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
return data;
|
|
1931
|
+
} catch (e) {
|
|
1932
|
+
return {
|
|
1933
|
+
status: 500,
|
|
1934
|
+
error: coalesce_to_error(e)
|
|
1935
|
+
};
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
/**
|
|
1940
|
+
* @param {Headers | Partial<import('types/helper').ResponseHeaders>} headers
|
|
1941
|
+
* @param {import('types/helper').JSONValue} body
|
|
1942
|
+
*/
|
|
1943
|
+
function validate_shadow_output(headers, body) {
|
|
1944
|
+
if (headers instanceof Headers && headers.has('set-cookie')) {
|
|
1945
|
+
throw new Error(
|
|
1946
|
+
'Shadow endpoint request handler cannot use Headers interface with Set-Cookie headers'
|
|
1947
|
+
);
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
if (!is_pojo(body)) {
|
|
1951
|
+
throw new Error('Body returned from shadow endpoint request handler must be a plain object');
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1766
1955
|
/**
|
|
1767
1956
|
* @typedef {import('./types.js').Loaded} Loaded
|
|
1768
1957
|
* @typedef {import('types/internal').SSROptions} SSROptions
|
|
@@ -1799,7 +1988,8 @@ async function respond_with_error({ event, options, state, $session, status, err
|
|
|
1799
1988
|
node: default_layout,
|
|
1800
1989
|
$session,
|
|
1801
1990
|
stuff: {},
|
|
1802
|
-
is_error: false
|
|
1991
|
+
is_error: false,
|
|
1992
|
+
is_leaf: false
|
|
1803
1993
|
})
|
|
1804
1994
|
);
|
|
1805
1995
|
|
|
@@ -1815,6 +2005,7 @@ async function respond_with_error({ event, options, state, $session, status, err
|
|
|
1815
2005
|
$session,
|
|
1816
2006
|
stuff: layout_loaded ? layout_loaded.stuff : {},
|
|
1817
2007
|
is_error: true,
|
|
2008
|
+
is_leaf: false,
|
|
1818
2009
|
status,
|
|
1819
2010
|
error
|
|
1820
2011
|
})
|
|
@@ -1947,7 +2138,8 @@ async function respond$1(opts) {
|
|
|
1947
2138
|
url: event.url,
|
|
1948
2139
|
node,
|
|
1949
2140
|
stuff,
|
|
1950
|
-
is_error: false
|
|
2141
|
+
is_error: false,
|
|
2142
|
+
is_leaf: i === nodes.length - 1
|
|
1951
2143
|
});
|
|
1952
2144
|
|
|
1953
2145
|
if (!loaded) return;
|
|
@@ -2002,6 +2194,7 @@ async function respond$1(opts) {
|
|
|
2002
2194
|
node: error_node,
|
|
2003
2195
|
stuff: node_loaded.stuff,
|
|
2004
2196
|
is_error: true,
|
|
2197
|
+
is_leaf: false,
|
|
2005
2198
|
status,
|
|
2006
2199
|
error
|
|
2007
2200
|
})
|
|
@@ -2115,13 +2308,12 @@ function with_cookies(response, set_cookie_headers) {
|
|
|
2115
2308
|
/**
|
|
2116
2309
|
* @param {import('types/hooks').RequestEvent} event
|
|
2117
2310
|
* @param {import('types/internal').SSRPage} route
|
|
2118
|
-
* @param {RegExpExecArray} match
|
|
2119
2311
|
* @param {import('types/internal').SSROptions} options
|
|
2120
2312
|
* @param {import('types/internal').SSRState} state
|
|
2121
2313
|
* @param {boolean} ssr
|
|
2122
2314
|
* @returns {Promise<Response | undefined>}
|
|
2123
2315
|
*/
|
|
2124
|
-
async function render_page(event, route,
|
|
2316
|
+
async function render_page(event, route, options, state, ssr) {
|
|
2125
2317
|
if (state.initiator === route) {
|
|
2126
2318
|
// infinite request cycle detected
|
|
2127
2319
|
return new Response(`Not found: ${event.url.pathname}`, {
|
|
@@ -2129,7 +2321,16 @@ async function render_page(event, route, match, options, state, ssr) {
|
|
|
2129
2321
|
});
|
|
2130
2322
|
}
|
|
2131
2323
|
|
|
2132
|
-
|
|
2324
|
+
if (route.shadow) {
|
|
2325
|
+
const type = negotiate(event.request.headers.get('accept') || 'text/html', [
|
|
2326
|
+
'text/html',
|
|
2327
|
+
'application/json'
|
|
2328
|
+
]);
|
|
2329
|
+
|
|
2330
|
+
if (type === 'application/json') {
|
|
2331
|
+
return render_endpoint(event, await route.shadow());
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2133
2334
|
|
|
2134
2335
|
const $session = await options.hooks.getSession(event);
|
|
2135
2336
|
|
|
@@ -2139,7 +2340,7 @@ async function render_page(event, route, match, options, state, ssr) {
|
|
|
2139
2340
|
state,
|
|
2140
2341
|
$session,
|
|
2141
2342
|
route,
|
|
2142
|
-
params,
|
|
2343
|
+
params: event.params, // TODO this is redundant
|
|
2143
2344
|
ssr
|
|
2144
2345
|
});
|
|
2145
2346
|
|
|
@@ -2158,6 +2359,60 @@ async function render_page(event, route, match, options, state, ssr) {
|
|
|
2158
2359
|
}
|
|
2159
2360
|
}
|
|
2160
2361
|
|
|
2362
|
+
/**
|
|
2363
|
+
* @param {string} accept
|
|
2364
|
+
* @param {string[]} types
|
|
2365
|
+
*/
|
|
2366
|
+
function negotiate(accept, types) {
|
|
2367
|
+
const parts = accept
|
|
2368
|
+
.split(',')
|
|
2369
|
+
.map((str, i) => {
|
|
2370
|
+
const match = /([^/]+)\/([^;]+)(?:;q=([0-9.]+))?/.exec(str);
|
|
2371
|
+
if (match) {
|
|
2372
|
+
const [, type, subtype, q = '1'] = match;
|
|
2373
|
+
return { type, subtype, q: +q, i };
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
throw new Error(`Invalid Accept header: ${accept}`);
|
|
2377
|
+
})
|
|
2378
|
+
.sort((a, b) => {
|
|
2379
|
+
if (a.q !== b.q) {
|
|
2380
|
+
return b.q - a.q;
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
if ((a.subtype === '*') !== (b.subtype === '*')) {
|
|
2384
|
+
return a.subtype === '*' ? 1 : -1;
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
if ((a.type === '*') !== (b.type === '*')) {
|
|
2388
|
+
return a.type === '*' ? 1 : -1;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
return a.i - b.i;
|
|
2392
|
+
});
|
|
2393
|
+
|
|
2394
|
+
let accepted;
|
|
2395
|
+
let min_priority = Infinity;
|
|
2396
|
+
|
|
2397
|
+
for (const mimetype of types) {
|
|
2398
|
+
const [type, subtype] = mimetype.split('/');
|
|
2399
|
+
const priority = parts.findIndex(
|
|
2400
|
+
(part) =>
|
|
2401
|
+
(part.type === type || part.type === '*') &&
|
|
2402
|
+
(part.subtype === subtype || part.subtype === '*')
|
|
2403
|
+
);
|
|
2404
|
+
|
|
2405
|
+
if (priority !== -1 && priority < min_priority) {
|
|
2406
|
+
accepted = mimetype;
|
|
2407
|
+
min_priority = priority;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
return accepted;
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
const DATA_SUFFIX = '/__data.json';
|
|
2415
|
+
|
|
2161
2416
|
/** @type {import('types/internal').Respond} */
|
|
2162
2417
|
async function respond(request, options, state = {}) {
|
|
2163
2418
|
const url = new URL(request.url);
|
|
@@ -2284,14 +2539,46 @@ async function respond(request, options, state = {}) {
|
|
|
2284
2539
|
decoded = decoded.slice(options.paths.base.length) || '/';
|
|
2285
2540
|
}
|
|
2286
2541
|
|
|
2542
|
+
const is_data_request = decoded.endsWith(DATA_SUFFIX);
|
|
2543
|
+
if (is_data_request) decoded = decoded.slice(0, -DATA_SUFFIX.length) || '/';
|
|
2544
|
+
|
|
2287
2545
|
for (const route of options.manifest._.routes) {
|
|
2288
2546
|
const match = route.pattern.exec(decoded);
|
|
2289
2547
|
if (!match) continue;
|
|
2290
2548
|
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2549
|
+
event.params = route.params ? decode_params(route.params(match)) : {};
|
|
2550
|
+
|
|
2551
|
+
/** @type {Response | undefined} */
|
|
2552
|
+
let response;
|
|
2553
|
+
|
|
2554
|
+
if (is_data_request && route.type === 'page' && route.shadow) {
|
|
2555
|
+
response = await render_endpoint(event, await route.shadow());
|
|
2556
|
+
|
|
2557
|
+
// since redirects are opaque to the browser, we need to repackage
|
|
2558
|
+
// 3xx responses as 200s with a custom header
|
|
2559
|
+
if (
|
|
2560
|
+
response &&
|
|
2561
|
+
response.status >= 300 &&
|
|
2562
|
+
response.status < 400 &&
|
|
2563
|
+
request.headers.get('x-sveltekit-noredirect') === 'true'
|
|
2564
|
+
) {
|
|
2565
|
+
const location = response.headers.get('location');
|
|
2566
|
+
|
|
2567
|
+
if (location) {
|
|
2568
|
+
response = new Response(undefined, {
|
|
2569
|
+
status: 204,
|
|
2570
|
+
headers: {
|
|
2571
|
+
'x-sveltekit-location': location
|
|
2572
|
+
}
|
|
2573
|
+
});
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
} else {
|
|
2577
|
+
response =
|
|
2578
|
+
route.type === 'endpoint'
|
|
2579
|
+
? await render_endpoint(event, await route.load())
|
|
2580
|
+
: await render_page(event, route, options, state, ssr);
|
|
2581
|
+
}
|
|
2295
2582
|
|
|
2296
2583
|
if (response) {
|
|
2297
2584
|
// respond with 304 if etag matches
|
package/dist/chunks/index.js
CHANGED
|
@@ -118,6 +118,12 @@ async function create_plugin(config, cwd) {
|
|
|
118
118
|
type: 'page',
|
|
119
119
|
pattern: route.pattern,
|
|
120
120
|
params: get_params(route.params),
|
|
121
|
+
shadow: route.shadow
|
|
122
|
+
? async () => {
|
|
123
|
+
const url = path__default.resolve(cwd, /** @type {string} */ (route.shadow));
|
|
124
|
+
return await vite.ssrLoadModule(url);
|
|
125
|
+
}
|
|
126
|
+
: null,
|
|
121
127
|
a: route.a.map((id) => manifest_data.components.indexOf(id)),
|
|
122
128
|
b: route.b.map((id) => manifest_data.components.indexOf(id))
|
|
123
129
|
};
|
package/dist/chunks/index2.js
CHANGED
|
@@ -165,7 +165,10 @@ function generate_client_manifest(manifest_data, base) {
|
|
|
165
165
|
'})';
|
|
166
166
|
|
|
167
167
|
const tuple = [route.pattern, get_indices(route.a), get_indices(route.b)];
|
|
168
|
-
|
|
168
|
+
|
|
169
|
+
// optional items
|
|
170
|
+
if (params || route.shadow) tuple.push(params || 'null');
|
|
171
|
+
if (route.shadow) tuple.push('1');
|
|
169
172
|
|
|
170
173
|
return `// ${route.a[route.a.length - 1]}\n\t\t[${tuple.join(', ')}]`;
|
|
171
174
|
}
|
|
@@ -382,6 +385,7 @@ var mime = new Mime(standard, other);
|
|
|
382
385
|
* }} Part
|
|
383
386
|
* @typedef {{
|
|
384
387
|
* basename: string;
|
|
388
|
+
* name: string;
|
|
385
389
|
* ext: string;
|
|
386
390
|
* parts: Part[],
|
|
387
391
|
* file: string;
|
|
@@ -427,12 +431,13 @@ function create_manifest_data({
|
|
|
427
431
|
|
|
428
432
|
/**
|
|
429
433
|
* @param {string} dir
|
|
434
|
+
* @param {string[]} parent_key
|
|
430
435
|
* @param {Part[][]} parent_segments
|
|
431
436
|
* @param {string[]} parent_params
|
|
432
437
|
* @param {Array<string|undefined>} layout_stack // accumulated __layout.svelte components
|
|
433
438
|
* @param {Array<string|undefined>} error_stack // accumulated __error.svelte components
|
|
434
439
|
*/
|
|
435
|
-
function walk(dir, parent_segments, parent_params, layout_stack, error_stack) {
|
|
440
|
+
function walk(dir, parent_key, parent_segments, parent_params, layout_stack, error_stack) {
|
|
436
441
|
/** @type {Item[]} */
|
|
437
442
|
let items = [];
|
|
438
443
|
fs__default.readdirSync(dir).forEach((basename) => {
|
|
@@ -482,6 +487,7 @@ function create_manifest_data({
|
|
|
482
487
|
|
|
483
488
|
items.push({
|
|
484
489
|
basename,
|
|
490
|
+
name,
|
|
485
491
|
ext,
|
|
486
492
|
parts,
|
|
487
493
|
file,
|
|
@@ -494,6 +500,7 @@ function create_manifest_data({
|
|
|
494
500
|
items = items.sort(comparator);
|
|
495
501
|
|
|
496
502
|
items.forEach((item) => {
|
|
503
|
+
const key = parent_key.slice();
|
|
497
504
|
const segments = parent_segments.slice();
|
|
498
505
|
|
|
499
506
|
if (item.is_index) {
|
|
@@ -517,11 +524,13 @@ function create_manifest_data({
|
|
|
517
524
|
}
|
|
518
525
|
|
|
519
526
|
segments[segments.length - 1] = last_segment;
|
|
527
|
+
key[key.length - 1] += item.route_suffix;
|
|
520
528
|
} else {
|
|
521
529
|
segments.push(item.parts);
|
|
522
530
|
}
|
|
523
531
|
}
|
|
524
532
|
} else {
|
|
533
|
+
key.push(item.name);
|
|
525
534
|
segments.push(item.parts);
|
|
526
535
|
}
|
|
527
536
|
|
|
@@ -555,6 +564,7 @@ function create_manifest_data({
|
|
|
555
564
|
|
|
556
565
|
walk(
|
|
557
566
|
path__default.join(dir, item.basename),
|
|
567
|
+
key,
|
|
558
568
|
segments,
|
|
559
569
|
params,
|
|
560
570
|
layout_reset ? [layout_reset] : layout_stack.concat(layout),
|
|
@@ -589,10 +599,12 @@ function create_manifest_data({
|
|
|
589
599
|
|
|
590
600
|
routes.push({
|
|
591
601
|
type: 'page',
|
|
602
|
+
key: key.join('/'),
|
|
592
603
|
segments: simple_segments,
|
|
593
604
|
pattern,
|
|
594
605
|
params,
|
|
595
606
|
path,
|
|
607
|
+
shadow: null,
|
|
596
608
|
a: /** @type {string[]} */ (concatenated),
|
|
597
609
|
b: /** @type {string[]} */ (errors)
|
|
598
610
|
});
|
|
@@ -601,6 +613,7 @@ function create_manifest_data({
|
|
|
601
613
|
|
|
602
614
|
routes.push({
|
|
603
615
|
type: 'endpoint',
|
|
616
|
+
key: key.join('/'),
|
|
604
617
|
segments: simple_segments,
|
|
605
618
|
pattern,
|
|
606
619
|
file: item.file,
|
|
@@ -617,7 +630,24 @@ function create_manifest_data({
|
|
|
617
630
|
|
|
618
631
|
components.push(layout, error);
|
|
619
632
|
|
|
620
|
-
walk(config.kit.files.routes, [], [], [layout], [error]);
|
|
633
|
+
walk(config.kit.files.routes, [], [], [], [layout], [error]);
|
|
634
|
+
|
|
635
|
+
// merge matching page/endpoint pairs into shadowed pages
|
|
636
|
+
let i = routes.length;
|
|
637
|
+
while (i--) {
|
|
638
|
+
const route = routes[i];
|
|
639
|
+
const prev = routes[i - 1];
|
|
640
|
+
|
|
641
|
+
if (prev && prev.key === route.key) {
|
|
642
|
+
if (prev.type !== 'endpoint' || route.type !== 'page') {
|
|
643
|
+
const relative = path__default.relative(cwd, path__default.resolve(config.kit.files.routes, prev.key));
|
|
644
|
+
throw new Error(`Duplicate route files: ${relative}`);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
route.shadow = prev.file;
|
|
648
|
+
routes.splice(--i, 1);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
621
651
|
|
|
622
652
|
const assets = fs__default.existsSync(config.kit.files.assets)
|
|
623
653
|
? list_files({ config, dir: config.kit.files.assets, path: '' })
|
package/dist/chunks/index3.js
CHANGED
|
@@ -383,8 +383,10 @@ async function build_server(
|
|
|
383
383
|
|
|
384
384
|
// add entry points for every endpoint...
|
|
385
385
|
manifest_data.routes.forEach((route) => {
|
|
386
|
-
|
|
387
|
-
|
|
386
|
+
const file = route.type === 'endpoint' ? route.file : route.shadow;
|
|
387
|
+
|
|
388
|
+
if (file) {
|
|
389
|
+
const resolved = path__default.resolve(cwd, file);
|
|
388
390
|
const relative = path__default.relative(config.kit.files.routes, resolved);
|
|
389
391
|
const name = posixify(path__default.join('entries/endpoints', relative.replace(/\.js$/, '')));
|
|
390
392
|
input[name] = resolved;
|
|
@@ -468,24 +470,6 @@ async function build_server(
|
|
|
468
470
|
|
|
469
471
|
const { chunks } = await create_build(merged_config);
|
|
470
472
|
|
|
471
|
-
/** @type {Record<string, string[]>} */
|
|
472
|
-
const lookup = {};
|
|
473
|
-
chunks.forEach((chunk) => {
|
|
474
|
-
if (!chunk.facadeModuleId) return;
|
|
475
|
-
const id = chunk.facadeModuleId.slice(cwd.length + 1);
|
|
476
|
-
lookup[id] = chunk.exports;
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
/** @type {Record<string, import('types/internal').HttpMethod[]>} */
|
|
480
|
-
const methods = {};
|
|
481
|
-
manifest_data.routes.forEach((route) => {
|
|
482
|
-
if (route.type === 'endpoint' && lookup[route.file]) {
|
|
483
|
-
methods[route.file] = lookup[route.file]
|
|
484
|
-
.map((x) => /** @type {import('types/internal').HttpMethod} */ (method_names[x]))
|
|
485
|
-
.filter(Boolean);
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
|
|
489
473
|
/** @type {import('vite').Manifest} */
|
|
490
474
|
const vite_manifest = JSON.parse(fs__default.readFileSync(`${output_dir}/server/manifest.json`, 'utf-8'));
|
|
491
475
|
|
|
@@ -576,8 +560,10 @@ function get_methods(cwd, output, manifest_data) {
|
|
|
576
560
|
/** @type {Record<string, import('types/internal').HttpMethod[]>} */
|
|
577
561
|
const methods = {};
|
|
578
562
|
manifest_data.routes.forEach((route) => {
|
|
579
|
-
|
|
580
|
-
|
|
563
|
+
const file = route.type === 'endpoint' ? route.file : route.shadow;
|
|
564
|
+
|
|
565
|
+
if (file && lookup[file]) {
|
|
566
|
+
methods[file] = lookup[file]
|
|
581
567
|
.map((x) => /** @type {import('types/internal').HttpMethod} */ (method_names[x]))
|
|
582
568
|
.filter(Boolean);
|
|
583
569
|
}
|
package/dist/chunks/index4.js
CHANGED
|
@@ -70,6 +70,7 @@ function generate_manifest(
|
|
|
70
70
|
pattern: ${route.pattern},
|
|
71
71
|
params: ${get_params(route.params)},
|
|
72
72
|
path: ${route.path ? s(route.path) : null},
|
|
73
|
+
shadow: ${route.shadow ? importer(`${relative_path}/${build_data.server.vite_manifest[route.shadow].file}`) : null},
|
|
73
74
|
a: ${s(route.a.map(component => component && bundled_nodes.get(component).index))},
|
|
74
75
|
b: ${s(route.b.map(component => component && bundled_nodes.get(component).index))}
|
|
75
76
|
}`.replace(/^\t\t/gm, '');
|
package/dist/cli.js
CHANGED
|
@@ -995,7 +995,7 @@ async function launch(port, https) {
|
|
|
995
995
|
exec(`${cmd} ${https ? 'https' : 'http'}://localhost:${port}`);
|
|
996
996
|
}
|
|
997
997
|
|
|
998
|
-
const prog = sade('svelte-kit').version('1.0.0-next.
|
|
998
|
+
const prog = sade('svelte-kit').version('1.0.0-next.261');
|
|
999
999
|
|
|
1000
1000
|
prog
|
|
1001
1001
|
.command('dev')
|
|
@@ -1153,7 +1153,7 @@ async function check_port(port) {
|
|
|
1153
1153
|
function welcome({ port, host, https, open, loose, allow, cwd }) {
|
|
1154
1154
|
if (open) launch(port, https);
|
|
1155
1155
|
|
|
1156
|
-
console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.
|
|
1156
|
+
console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.261'}\n`));
|
|
1157
1157
|
|
|
1158
1158
|
const protocol = https ? 'https:' : 'http:';
|
|
1159
1159
|
const exposed = typeof host !== 'undefined' && host !== 'localhost' && host !== '127.0.0.1';
|
package/package.json
CHANGED
package/types/endpoint.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequestEvent } from './hooks';
|
|
2
|
-
import { Either, JSONValue, MaybePromise, ResponseHeaders } from './helper';
|
|
2
|
+
import { Either, JSONObject, JSONValue, MaybePromise, ResponseHeaders } from './helper';
|
|
3
3
|
|
|
4
4
|
type Body = JSONValue | Uint8Array | ReadableStream | import('stream').Readable;
|
|
5
5
|
|
|
@@ -14,5 +14,26 @@ export interface Fallthrough {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export interface RequestHandler<Output extends Body = Body> {
|
|
17
|
-
(event: RequestEvent): MaybePromise<
|
|
17
|
+
(event: RequestEvent): MaybePromise<
|
|
18
|
+
Either<Output extends Response ? Response : EndpointOutput<Output>, Fallthrough>
|
|
19
|
+
>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ShadowEndpointOutput<Output extends JSONObject = JSONObject> {
|
|
23
|
+
status?: number;
|
|
24
|
+
headers?: Partial<ResponseHeaders>;
|
|
25
|
+
body?: Output;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ShadowRequestHandler<Output extends JSONObject = JSONObject> {
|
|
29
|
+
(event: RequestEvent): MaybePromise<Either<ShadowEndpointOutput<Output>, Fallthrough>>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ShadowData {
|
|
33
|
+
fallthrough?: boolean;
|
|
34
|
+
status?: number;
|
|
35
|
+
error?: Error;
|
|
36
|
+
redirect?: string;
|
|
37
|
+
cookies?: string[];
|
|
38
|
+
body?: JSONObject;
|
|
18
39
|
}
|
package/types/helper.d.ts
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
type ToJSON = { toJSON(...args: any[]): Exclude<JSONValue, ToJSON> };
|
|
2
2
|
|
|
3
|
-
export type JSONValue
|
|
4
|
-
|
|
5
|
-
| number
|
|
6
|
-
| boolean
|
|
7
|
-
| null
|
|
8
|
-
| ToJSON
|
|
9
|
-
| JSONValue[]
|
|
10
|
-
| { [key: string]: JSONValue };
|
|
3
|
+
export type JSONObject = { [key: string]: JSONValue };
|
|
4
|
+
export type JSONValue = string | number | boolean | null | ToJSON | JSONValue[] | JSONObject;
|
|
11
5
|
|
|
12
6
|
/** `string[]` is only for set-cookie, everything else must be type of `string` */
|
|
13
7
|
export type ResponseHeaders = Record<string, string | string[]>;
|
package/types/internal.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { OutputAsset, OutputChunk } from 'rollup';
|
|
2
2
|
import { ValidatedConfig } from './config';
|
|
3
3
|
import { InternalApp, SSRManifest } from './app';
|
|
4
|
-
import { Fallthrough, RequestHandler } from './endpoint';
|
|
4
|
+
import { Fallthrough, RequestHandler, ShadowRequestHandler } from './endpoint';
|
|
5
5
|
import { Either } from './helper';
|
|
6
6
|
import { ExternalFetch, GetSession, Handle, HandleError, RequestEvent } from './hooks';
|
|
7
7
|
import { Load } from './page';
|
|
@@ -74,6 +74,11 @@ export interface SSRPage {
|
|
|
74
74
|
type: 'page';
|
|
75
75
|
pattern: RegExp;
|
|
76
76
|
params: GetParams;
|
|
77
|
+
shadow:
|
|
78
|
+
| null
|
|
79
|
+
| (() => Promise<{
|
|
80
|
+
[method: string]: ShadowRequestHandler;
|
|
81
|
+
}>);
|
|
77
82
|
/**
|
|
78
83
|
* plan a is to render 1 or more layout components followed by a leaf component.
|
|
79
84
|
*/
|
|
@@ -96,7 +101,8 @@ export interface SSREndpoint {
|
|
|
96
101
|
|
|
97
102
|
export type SSRRoute = SSREndpoint | SSRPage;
|
|
98
103
|
|
|
99
|
-
|
|
104
|
+
type HasShadow = 1;
|
|
105
|
+
export type CSRRoute = [RegExp, CSRComponentLoader[], CSRComponentLoader[], GetParams?, HasShadow?];
|
|
100
106
|
|
|
101
107
|
export type SSRNodeLoader = () => Promise<SSRNode>;
|
|
102
108
|
|
|
@@ -179,6 +185,8 @@ export type HttpMethod = 'get' | 'head' | 'post' | 'put' | 'delete' | 'patch';
|
|
|
179
185
|
|
|
180
186
|
export interface PageData {
|
|
181
187
|
type: 'page';
|
|
188
|
+
key: string;
|
|
189
|
+
shadow: string | null;
|
|
182
190
|
segments: RouteSegment[];
|
|
183
191
|
pattern: RegExp;
|
|
184
192
|
params: string[];
|
|
@@ -189,6 +197,7 @@ export interface PageData {
|
|
|
189
197
|
|
|
190
198
|
export interface EndpointData {
|
|
191
199
|
type: 'endpoint';
|
|
200
|
+
key: string;
|
|
192
201
|
segments: RouteSegment[];
|
|
193
202
|
pattern: RegExp;
|
|
194
203
|
params: string[];
|
package/types/page.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Either, MaybePromise } from './helper';
|
|
|
4
4
|
export interface LoadInput<Params = Record<string, string>> {
|
|
5
5
|
url: URL;
|
|
6
6
|
params: Params;
|
|
7
|
+
props: Record<string, any>;
|
|
7
8
|
fetch(info: RequestInfo, init?: RequestInit): Promise<Response>;
|
|
8
9
|
session: App.Session;
|
|
9
10
|
stuff: Partial<App.Stuff>;
|