@sveltejs/kit 1.0.0-next.24 → 1.0.0-next.240
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/README.md +12 -9
- package/assets/app/env.js +20 -0
- package/assets/app/navigation.js +79 -0
- package/assets/app/paths.js +1 -0
- package/assets/{runtime/app → app}/stores.js +19 -15
- package/assets/chunks/utils.js +13 -0
- package/assets/client/singletons.js +21 -0
- package/assets/client/start.js +1382 -0
- package/assets/components/error.svelte +19 -3
- package/assets/env.js +8 -0
- package/assets/paths.js +13 -0
- package/assets/server/index.js +1842 -0
- package/dist/chunks/build.js +658 -0
- package/dist/chunks/cert.js +28154 -0
- package/dist/chunks/index.js +2202 -0
- package/dist/chunks/index2.js +807 -0
- package/dist/chunks/index3.js +643 -0
- package/dist/chunks/index4.js +109 -0
- package/dist/chunks/index5.js +752 -0
- package/dist/chunks/index6.js +170 -0
- package/dist/chunks/index7.js +15574 -0
- package/dist/chunks/index8.js +4207 -0
- package/dist/chunks/misc.js +3 -0
- package/dist/chunks/multipart-parser.js +449 -0
- package/dist/chunks/url.js +119 -0
- package/dist/cli.js +1060 -84
- package/dist/hooks.js +28 -0
- package/dist/install-fetch.js +6518 -0
- package/dist/node.js +85 -0
- package/package.json +95 -54
- package/svelte-kit.js +2 -0
- package/types/ambient-modules.d.ts +201 -0
- package/types/app.d.ts +31 -0
- package/types/config.d.ts +164 -0
- package/types/endpoint.d.ts +18 -0
- package/types/helper.d.ts +32 -0
- package/types/hooks.d.ts +42 -0
- package/types/index.d.ts +10 -0
- package/types/internal.d.ts +234 -0
- package/types/page.d.ts +85 -0
- package/CHANGELOG.md +0 -300
- package/assets/runtime/app/navigation.js +0 -23
- package/assets/runtime/app/navigation.js.map +0 -1
- package/assets/runtime/app/paths.js +0 -2
- package/assets/runtime/app/paths.js.map +0 -1
- package/assets/runtime/app/stores.js.map +0 -1
- package/assets/runtime/internal/singletons.js +0 -15
- package/assets/runtime/internal/singletons.js.map +0 -1
- package/assets/runtime/internal/start.js +0 -591
- package/assets/runtime/internal/start.js.map +0 -1
- package/assets/runtime/utils-85ebcc60.js +0 -18
- package/assets/runtime/utils-85ebcc60.js.map +0 -1
- package/dist/api.js +0 -44
- package/dist/api.js.map +0 -1
- package/dist/build.js +0 -246
- package/dist/build.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/colors.js +0 -37
- package/dist/colors.js.map +0 -1
- package/dist/create_app.js +0 -578
- package/dist/create_app.js.map +0 -1
- package/dist/index.js +0 -367
- package/dist/index.js.map +0 -1
- package/dist/index2.js +0 -12044
- package/dist/index2.js.map +0 -1
- package/dist/index3.js +0 -547
- package/dist/index3.js.map +0 -1
- package/dist/index4.js +0 -73
- package/dist/index4.js.map +0 -1
- package/dist/index5.js +0 -464
- package/dist/index5.js.map +0 -1
- package/dist/index6.js +0 -727
- package/dist/index6.js.map +0 -1
- package/dist/logging.js +0 -43
- package/dist/logging.js.map +0 -1
- package/dist/package.js +0 -432
- package/dist/package.js.map +0 -1
- package/dist/renderer.js +0 -2391
- package/dist/renderer.js.map +0 -1
- package/dist/standard.js +0 -101
- package/dist/standard.js.map +0 -1
- package/dist/utils.js +0 -54
- package/dist/utils.js.map +0 -1
- package/svelte-kit +0 -3
|
@@ -0,0 +1,2202 @@
|
|
|
1
|
+
import path__default from 'path';
|
|
2
|
+
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
3
|
+
import vite from 'vite';
|
|
4
|
+
import { c as create_manifest_data, a as create_app, d as deep_merge } from './index2.js';
|
|
5
|
+
import { c as coalesce_to_error, S as SVELTE_KIT_ASSETS, r as resolve_entry, $, a as SVELTE_KIT, b as runtime, l as load_template, g as get_mime_lookup, d as copy_assets, e as get_aliases, p as print_config_conflicts } from '../cli.js';
|
|
6
|
+
import fs__default from 'fs';
|
|
7
|
+
import { URL as URL$1 } from 'url';
|
|
8
|
+
import { s as sirv } from './build.js';
|
|
9
|
+
import { e as escape_html_attr, r as resolve, i as is_root_relative, a as escape_json_string_in_html } from './url.js';
|
|
10
|
+
import { s } from './misc.js';
|
|
11
|
+
import { __fetch_polyfill } from '../install-fetch.js';
|
|
12
|
+
import { getRequest, setResponse } from '../node.js';
|
|
13
|
+
import 'sade';
|
|
14
|
+
import 'child_process';
|
|
15
|
+
import 'net';
|
|
16
|
+
import 'os';
|
|
17
|
+
import 'querystring';
|
|
18
|
+
import 'node:http';
|
|
19
|
+
import 'node:https';
|
|
20
|
+
import 'node:zlib';
|
|
21
|
+
import 'node:stream';
|
|
22
|
+
import 'node:util';
|
|
23
|
+
import 'node:url';
|
|
24
|
+
import 'stream';
|
|
25
|
+
|
|
26
|
+
/** @param {Partial<import('types/helper').ResponseHeaders> | undefined} object */
|
|
27
|
+
function to_headers(object) {
|
|
28
|
+
const headers = new Headers();
|
|
29
|
+
|
|
30
|
+
if (object) {
|
|
31
|
+
for (const key in object) {
|
|
32
|
+
const value = object[key];
|
|
33
|
+
if (!value) continue;
|
|
34
|
+
|
|
35
|
+
if (typeof value === 'string') {
|
|
36
|
+
headers.set(key, value);
|
|
37
|
+
} else {
|
|
38
|
+
value.forEach((value) => {
|
|
39
|
+
headers.append(key, value);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return headers;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Hash using djb2
|
|
50
|
+
* @param {import('types/hooks').StrictBody} value
|
|
51
|
+
*/
|
|
52
|
+
function hash(value) {
|
|
53
|
+
let hash = 5381;
|
|
54
|
+
let i = value.length;
|
|
55
|
+
|
|
56
|
+
if (typeof value === 'string') {
|
|
57
|
+
while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
|
|
58
|
+
} else {
|
|
59
|
+
while (i) hash = (hash * 33) ^ value[--i];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (hash >>> 0).toString(36);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** @param {Record<string, any>} obj */
|
|
66
|
+
|
|
67
|
+
/** @param {Record<string, string>} params */
|
|
68
|
+
function decode_params(params) {
|
|
69
|
+
for (const key in params) {
|
|
70
|
+
// input has already been decoded by decodeURI
|
|
71
|
+
// now handle the rest that decodeURIComponent would do
|
|
72
|
+
params[key] = params[key]
|
|
73
|
+
.replace(/%23/g, '#')
|
|
74
|
+
.replace(/%3[Bb]/g, ';')
|
|
75
|
+
.replace(/%2[Cc]/g, ',')
|
|
76
|
+
.replace(/%2[Ff]/g, '/')
|
|
77
|
+
.replace(/%3[Ff]/g, '?')
|
|
78
|
+
.replace(/%3[Aa]/g, ':')
|
|
79
|
+
.replace(/%40/g, '@')
|
|
80
|
+
.replace(/%26/g, '&')
|
|
81
|
+
.replace(/%3[Dd]/g, '=')
|
|
82
|
+
.replace(/%2[Bb]/g, '+')
|
|
83
|
+
.replace(/%24/g, '$');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return params;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** @param {string} body */
|
|
90
|
+
function error(body) {
|
|
91
|
+
return new Response(body, {
|
|
92
|
+
status: 500
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** @param {unknown} s */
|
|
97
|
+
function is_string(s) {
|
|
98
|
+
return typeof s === 'string' || s instanceof String;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const text_types = new Set([
|
|
102
|
+
'application/xml',
|
|
103
|
+
'application/json',
|
|
104
|
+
'application/x-www-form-urlencoded',
|
|
105
|
+
'multipart/form-data'
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Decides how the body should be parsed based on its mime type. Should match what's in parse_body
|
|
110
|
+
*
|
|
111
|
+
* @param {string | undefined | null} content_type The `content-type` header of a request/response.
|
|
112
|
+
* @returns {boolean}
|
|
113
|
+
*/
|
|
114
|
+
function is_text(content_type) {
|
|
115
|
+
if (!content_type) return true; // defaults to json
|
|
116
|
+
const type = content_type.split(';')[0].toLowerCase(); // get the mime type
|
|
117
|
+
|
|
118
|
+
return type.startsWith('text/') || type.endsWith('+xml') || text_types.has(type);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @param {import('types/hooks').RequestEvent} event
|
|
123
|
+
* @param {import('types/internal').SSREndpoint} route
|
|
124
|
+
* @param {RegExpExecArray} match
|
|
125
|
+
* @returns {Promise<Response | undefined>}
|
|
126
|
+
*/
|
|
127
|
+
async function render_endpoint(event, route, match) {
|
|
128
|
+
const mod = await route.load();
|
|
129
|
+
|
|
130
|
+
/** @type {import('types/endpoint').RequestHandler} */
|
|
131
|
+
const handler = mod[event.request.method.toLowerCase().replace('delete', 'del')]; // 'delete' is a reserved word
|
|
132
|
+
|
|
133
|
+
if (!handler) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// we're mutating `request` so that we don't have to do { ...request, params }
|
|
138
|
+
// on the next line, since that breaks the getters that replace path, query and
|
|
139
|
+
// origin. We could revert that once we remove the getters
|
|
140
|
+
event.params = route.params ? decode_params(route.params(match)) : {};
|
|
141
|
+
|
|
142
|
+
const response = await handler(event);
|
|
143
|
+
const preface = `Invalid response from route ${event.url.pathname}`;
|
|
144
|
+
|
|
145
|
+
if (typeof response !== 'object') {
|
|
146
|
+
return error(`${preface}: expected an object, got ${typeof response}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (response.fallthrough) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const { status = 200, body = {} } = response;
|
|
154
|
+
const headers =
|
|
155
|
+
response.headers instanceof Headers ? response.headers : to_headers(response.headers);
|
|
156
|
+
|
|
157
|
+
const type = headers.get('content-type');
|
|
158
|
+
|
|
159
|
+
if (!is_text(type) && !(body instanceof Uint8Array || is_string(body))) {
|
|
160
|
+
return error(
|
|
161
|
+
`${preface}: body must be an instance of string or Uint8Array if content-type is not a supported textual content-type`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** @type {import('types/hooks').StrictBody} */
|
|
166
|
+
let normalized_body;
|
|
167
|
+
|
|
168
|
+
if (is_pojo(body) && (!type || type.startsWith('application/json'))) {
|
|
169
|
+
headers.set('content-type', 'application/json; charset=utf-8');
|
|
170
|
+
normalized_body = JSON.stringify(body);
|
|
171
|
+
} else {
|
|
172
|
+
normalized_body = /** @type {import('types/hooks').StrictBody} */ (body);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (
|
|
176
|
+
(typeof normalized_body === 'string' || normalized_body instanceof Uint8Array) &&
|
|
177
|
+
!headers.has('etag')
|
|
178
|
+
) {
|
|
179
|
+
const cache_control = headers.get('cache-control');
|
|
180
|
+
if (!cache_control || !/(no-store|immutable)/.test(cache_control)) {
|
|
181
|
+
headers.set('etag', `"${hash(normalized_body)}"`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return new Response(normalized_body, {
|
|
186
|
+
status,
|
|
187
|
+
headers
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** @param {any} body */
|
|
192
|
+
function is_pojo(body) {
|
|
193
|
+
if (typeof body !== 'object') return false;
|
|
194
|
+
|
|
195
|
+
if (body) {
|
|
196
|
+
if (body instanceof Uint8Array) return false;
|
|
197
|
+
|
|
198
|
+
// body could be a node Readable, but we don't want to import
|
|
199
|
+
// node built-ins, so we use duck typing
|
|
200
|
+
if (body._readableState && body._writableState && body._events) return false;
|
|
201
|
+
|
|
202
|
+
// similarly, it could be a web ReadableStream
|
|
203
|
+
if (body[Symbol.toStringTag] === 'ReadableStream') return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$';
|
|
210
|
+
var unsafeChars = /[<>\b\f\n\r\t\0\u2028\u2029]/g;
|
|
211
|
+
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)$/;
|
|
212
|
+
var escaped = {
|
|
213
|
+
'<': '\\u003C',
|
|
214
|
+
'>': '\\u003E',
|
|
215
|
+
'/': '\\u002F',
|
|
216
|
+
'\\': '\\\\',
|
|
217
|
+
'\b': '\\b',
|
|
218
|
+
'\f': '\\f',
|
|
219
|
+
'\n': '\\n',
|
|
220
|
+
'\r': '\\r',
|
|
221
|
+
'\t': '\\t',
|
|
222
|
+
'\0': '\\0',
|
|
223
|
+
'\u2028': '\\u2028',
|
|
224
|
+
'\u2029': '\\u2029'
|
|
225
|
+
};
|
|
226
|
+
var objectProtoOwnPropertyNames = Object.getOwnPropertyNames(Object.prototype).sort().join('\0');
|
|
227
|
+
function devalue(value) {
|
|
228
|
+
var counts = new Map();
|
|
229
|
+
function walk(thing) {
|
|
230
|
+
if (typeof thing === 'function') {
|
|
231
|
+
throw new Error("Cannot stringify a function");
|
|
232
|
+
}
|
|
233
|
+
if (counts.has(thing)) {
|
|
234
|
+
counts.set(thing, counts.get(thing) + 1);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
counts.set(thing, 1);
|
|
238
|
+
if (!isPrimitive(thing)) {
|
|
239
|
+
var type = getType(thing);
|
|
240
|
+
switch (type) {
|
|
241
|
+
case 'Number':
|
|
242
|
+
case 'String':
|
|
243
|
+
case 'Boolean':
|
|
244
|
+
case 'Date':
|
|
245
|
+
case 'RegExp':
|
|
246
|
+
return;
|
|
247
|
+
case 'Array':
|
|
248
|
+
thing.forEach(walk);
|
|
249
|
+
break;
|
|
250
|
+
case 'Set':
|
|
251
|
+
case 'Map':
|
|
252
|
+
Array.from(thing).forEach(walk);
|
|
253
|
+
break;
|
|
254
|
+
default:
|
|
255
|
+
var proto = Object.getPrototypeOf(thing);
|
|
256
|
+
if (proto !== Object.prototype &&
|
|
257
|
+
proto !== null &&
|
|
258
|
+
Object.getOwnPropertyNames(proto).sort().join('\0') !== objectProtoOwnPropertyNames) {
|
|
259
|
+
throw new Error("Cannot stringify arbitrary non-POJOs");
|
|
260
|
+
}
|
|
261
|
+
if (Object.getOwnPropertySymbols(thing).length > 0) {
|
|
262
|
+
throw new Error("Cannot stringify POJOs with symbolic keys");
|
|
263
|
+
}
|
|
264
|
+
Object.keys(thing).forEach(function (key) { return walk(thing[key]); });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
walk(value);
|
|
269
|
+
var names = new Map();
|
|
270
|
+
Array.from(counts)
|
|
271
|
+
.filter(function (entry) { return entry[1] > 1; })
|
|
272
|
+
.sort(function (a, b) { return b[1] - a[1]; })
|
|
273
|
+
.forEach(function (entry, i) {
|
|
274
|
+
names.set(entry[0], getName(i));
|
|
275
|
+
});
|
|
276
|
+
function stringify(thing) {
|
|
277
|
+
if (names.has(thing)) {
|
|
278
|
+
return names.get(thing);
|
|
279
|
+
}
|
|
280
|
+
if (isPrimitive(thing)) {
|
|
281
|
+
return stringifyPrimitive(thing);
|
|
282
|
+
}
|
|
283
|
+
var type = getType(thing);
|
|
284
|
+
switch (type) {
|
|
285
|
+
case 'Number':
|
|
286
|
+
case 'String':
|
|
287
|
+
case 'Boolean':
|
|
288
|
+
return "Object(" + stringify(thing.valueOf()) + ")";
|
|
289
|
+
case 'RegExp':
|
|
290
|
+
return "new RegExp(" + stringifyString(thing.source) + ", \"" + thing.flags + "\")";
|
|
291
|
+
case 'Date':
|
|
292
|
+
return "new Date(" + thing.getTime() + ")";
|
|
293
|
+
case 'Array':
|
|
294
|
+
var members = thing.map(function (v, i) { return i in thing ? stringify(v) : ''; });
|
|
295
|
+
var tail = thing.length === 0 || (thing.length - 1 in thing) ? '' : ',';
|
|
296
|
+
return "[" + members.join(',') + tail + "]";
|
|
297
|
+
case 'Set':
|
|
298
|
+
case 'Map':
|
|
299
|
+
return "new " + type + "([" + Array.from(thing).map(stringify).join(',') + "])";
|
|
300
|
+
default:
|
|
301
|
+
var obj = "{" + Object.keys(thing).map(function (key) { return safeKey(key) + ":" + stringify(thing[key]); }).join(',') + "}";
|
|
302
|
+
var proto = Object.getPrototypeOf(thing);
|
|
303
|
+
if (proto === null) {
|
|
304
|
+
return Object.keys(thing).length > 0
|
|
305
|
+
? "Object.assign(Object.create(null)," + obj + ")"
|
|
306
|
+
: "Object.create(null)";
|
|
307
|
+
}
|
|
308
|
+
return obj;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
var str = stringify(value);
|
|
312
|
+
if (names.size) {
|
|
313
|
+
var params_1 = [];
|
|
314
|
+
var statements_1 = [];
|
|
315
|
+
var values_1 = [];
|
|
316
|
+
names.forEach(function (name, thing) {
|
|
317
|
+
params_1.push(name);
|
|
318
|
+
if (isPrimitive(thing)) {
|
|
319
|
+
values_1.push(stringifyPrimitive(thing));
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
var type = getType(thing);
|
|
323
|
+
switch (type) {
|
|
324
|
+
case 'Number':
|
|
325
|
+
case 'String':
|
|
326
|
+
case 'Boolean':
|
|
327
|
+
values_1.push("Object(" + stringify(thing.valueOf()) + ")");
|
|
328
|
+
break;
|
|
329
|
+
case 'RegExp':
|
|
330
|
+
values_1.push(thing.toString());
|
|
331
|
+
break;
|
|
332
|
+
case 'Date':
|
|
333
|
+
values_1.push("new Date(" + thing.getTime() + ")");
|
|
334
|
+
break;
|
|
335
|
+
case 'Array':
|
|
336
|
+
values_1.push("Array(" + thing.length + ")");
|
|
337
|
+
thing.forEach(function (v, i) {
|
|
338
|
+
statements_1.push(name + "[" + i + "]=" + stringify(v));
|
|
339
|
+
});
|
|
340
|
+
break;
|
|
341
|
+
case 'Set':
|
|
342
|
+
values_1.push("new Set");
|
|
343
|
+
statements_1.push(name + "." + Array.from(thing).map(function (v) { return "add(" + stringify(v) + ")"; }).join('.'));
|
|
344
|
+
break;
|
|
345
|
+
case 'Map':
|
|
346
|
+
values_1.push("new Map");
|
|
347
|
+
statements_1.push(name + "." + Array.from(thing).map(function (_a) {
|
|
348
|
+
var k = _a[0], v = _a[1];
|
|
349
|
+
return "set(" + stringify(k) + ", " + stringify(v) + ")";
|
|
350
|
+
}).join('.'));
|
|
351
|
+
break;
|
|
352
|
+
default:
|
|
353
|
+
values_1.push(Object.getPrototypeOf(thing) === null ? 'Object.create(null)' : '{}');
|
|
354
|
+
Object.keys(thing).forEach(function (key) {
|
|
355
|
+
statements_1.push("" + name + safeProp(key) + "=" + stringify(thing[key]));
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
statements_1.push("return " + str);
|
|
360
|
+
return "(function(" + params_1.join(',') + "){" + statements_1.join(';') + "}(" + values_1.join(',') + "))";
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
return str;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function getName(num) {
|
|
367
|
+
var name = '';
|
|
368
|
+
do {
|
|
369
|
+
name = chars[num % chars.length] + name;
|
|
370
|
+
num = ~~(num / chars.length) - 1;
|
|
371
|
+
} while (num >= 0);
|
|
372
|
+
return reserved.test(name) ? name + "_" : name;
|
|
373
|
+
}
|
|
374
|
+
function isPrimitive(thing) {
|
|
375
|
+
return Object(thing) !== thing;
|
|
376
|
+
}
|
|
377
|
+
function stringifyPrimitive(thing) {
|
|
378
|
+
if (typeof thing === 'string')
|
|
379
|
+
return stringifyString(thing);
|
|
380
|
+
if (thing === void 0)
|
|
381
|
+
return 'void 0';
|
|
382
|
+
if (thing === 0 && 1 / thing < 0)
|
|
383
|
+
return '-0';
|
|
384
|
+
var str = String(thing);
|
|
385
|
+
if (typeof thing === 'number')
|
|
386
|
+
return str.replace(/^(-)?0\./, '$1.');
|
|
387
|
+
return str;
|
|
388
|
+
}
|
|
389
|
+
function getType(thing) {
|
|
390
|
+
return Object.prototype.toString.call(thing).slice(8, -1);
|
|
391
|
+
}
|
|
392
|
+
function escapeUnsafeChar(c) {
|
|
393
|
+
return escaped[c] || c;
|
|
394
|
+
}
|
|
395
|
+
function escapeUnsafeChars(str) {
|
|
396
|
+
return str.replace(unsafeChars, escapeUnsafeChar);
|
|
397
|
+
}
|
|
398
|
+
function safeKey(key) {
|
|
399
|
+
return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key) ? key : escapeUnsafeChars(JSON.stringify(key));
|
|
400
|
+
}
|
|
401
|
+
function safeProp(key) {
|
|
402
|
+
return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key) ? "." + key : "[" + escapeUnsafeChars(JSON.stringify(key)) + "]";
|
|
403
|
+
}
|
|
404
|
+
function stringifyString(str) {
|
|
405
|
+
var result = '"';
|
|
406
|
+
for (var i = 0; i < str.length; i += 1) {
|
|
407
|
+
var char = str.charAt(i);
|
|
408
|
+
var code = char.charCodeAt(0);
|
|
409
|
+
if (char === '"') {
|
|
410
|
+
result += '\\"';
|
|
411
|
+
}
|
|
412
|
+
else if (char in escaped) {
|
|
413
|
+
result += escaped[char];
|
|
414
|
+
}
|
|
415
|
+
else if (code >= 0xd800 && code <= 0xdfff) {
|
|
416
|
+
var next = str.charCodeAt(i + 1);
|
|
417
|
+
// If this is the beginning of a [high, low] surrogate pair,
|
|
418
|
+
// add the next two characters, otherwise escape
|
|
419
|
+
if (code <= 0xdbff && (next >= 0xdc00 && next <= 0xdfff)) {
|
|
420
|
+
result += char + str[++i];
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
result += "\\u" + code.toString(16).toUpperCase();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
result += char;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
result += '"';
|
|
431
|
+
return result;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function noop() { }
|
|
435
|
+
function safe_not_equal(a, b) {
|
|
436
|
+
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
|
|
437
|
+
}
|
|
438
|
+
Promise.resolve();
|
|
439
|
+
|
|
440
|
+
const subscriber_queue = [];
|
|
441
|
+
/**
|
|
442
|
+
* Create a `Writable` store that allows both updating and reading by subscription.
|
|
443
|
+
* @param {*=}value initial value
|
|
444
|
+
* @param {StartStopNotifier=}start start and stop notifications for subscriptions
|
|
445
|
+
*/
|
|
446
|
+
function writable(value, start = noop) {
|
|
447
|
+
let stop;
|
|
448
|
+
const subscribers = new Set();
|
|
449
|
+
function set(new_value) {
|
|
450
|
+
if (safe_not_equal(value, new_value)) {
|
|
451
|
+
value = new_value;
|
|
452
|
+
if (stop) { // store is ready
|
|
453
|
+
const run_queue = !subscriber_queue.length;
|
|
454
|
+
for (const subscriber of subscribers) {
|
|
455
|
+
subscriber[1]();
|
|
456
|
+
subscriber_queue.push(subscriber, value);
|
|
457
|
+
}
|
|
458
|
+
if (run_queue) {
|
|
459
|
+
for (let i = 0; i < subscriber_queue.length; i += 2) {
|
|
460
|
+
subscriber_queue[i][0](subscriber_queue[i + 1]);
|
|
461
|
+
}
|
|
462
|
+
subscriber_queue.length = 0;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function update(fn) {
|
|
468
|
+
set(fn(value));
|
|
469
|
+
}
|
|
470
|
+
function subscribe(run, invalidate = noop) {
|
|
471
|
+
const subscriber = [run, invalidate];
|
|
472
|
+
subscribers.add(subscriber);
|
|
473
|
+
if (subscribers.size === 1) {
|
|
474
|
+
stop = start(set) || noop;
|
|
475
|
+
}
|
|
476
|
+
run(value);
|
|
477
|
+
return () => {
|
|
478
|
+
subscribers.delete(subscriber);
|
|
479
|
+
if (subscribers.size === 0) {
|
|
480
|
+
stop();
|
|
481
|
+
stop = null;
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
return { set, update, subscribe };
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/** @param {URL} url */
|
|
489
|
+
function create_prerendering_url_proxy(url) {
|
|
490
|
+
return new Proxy(url, {
|
|
491
|
+
get: (target, prop, receiver) => {
|
|
492
|
+
if (prop === 'search' || prop === 'searchParams') {
|
|
493
|
+
throw new Error(`Cannot access url.${prop} on a page with prerendering enabled`);
|
|
494
|
+
}
|
|
495
|
+
return Reflect.get(target, prop, receiver);
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// TODO rename this function/module
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* @param {{
|
|
504
|
+
* branch: Array<import('./types').Loaded>;
|
|
505
|
+
* options: import('types/internal').SSRRenderOptions;
|
|
506
|
+
* state: import('types/internal').SSRRenderState;
|
|
507
|
+
* $session: any;
|
|
508
|
+
* page_config: { hydrate: boolean, router: boolean };
|
|
509
|
+
* status: number;
|
|
510
|
+
* error?: Error;
|
|
511
|
+
* url: URL;
|
|
512
|
+
* params: Record<string, string>;
|
|
513
|
+
* ssr: boolean;
|
|
514
|
+
* stuff: Record<string, any>;
|
|
515
|
+
* }} opts
|
|
516
|
+
*/
|
|
517
|
+
async function render_response({
|
|
518
|
+
branch,
|
|
519
|
+
options,
|
|
520
|
+
state,
|
|
521
|
+
$session,
|
|
522
|
+
page_config,
|
|
523
|
+
status,
|
|
524
|
+
error,
|
|
525
|
+
url,
|
|
526
|
+
params,
|
|
527
|
+
ssr,
|
|
528
|
+
stuff
|
|
529
|
+
}) {
|
|
530
|
+
const css = new Set(options.manifest._.entry.css);
|
|
531
|
+
const js = new Set(options.manifest._.entry.js);
|
|
532
|
+
/** @type {Map<string, string>} */
|
|
533
|
+
const styles = new Map();
|
|
534
|
+
|
|
535
|
+
/** @type {Array<{ url: string, body: string, json: string }>} */
|
|
536
|
+
const serialized_data = [];
|
|
537
|
+
|
|
538
|
+
let rendered;
|
|
539
|
+
|
|
540
|
+
let is_private = false;
|
|
541
|
+
let maxage;
|
|
542
|
+
|
|
543
|
+
if (error) {
|
|
544
|
+
error.stack = options.get_stack(error);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (ssr) {
|
|
548
|
+
branch.forEach(({ node, loaded, fetched, uses_credentials }) => {
|
|
549
|
+
if (node.css) node.css.forEach((url) => css.add(url));
|
|
550
|
+
if (node.js) node.js.forEach((url) => js.add(url));
|
|
551
|
+
if (node.styles) Object.entries(node.styles).forEach(([k, v]) => styles.set(k, v));
|
|
552
|
+
|
|
553
|
+
// TODO probably better if `fetched` wasn't populated unless `hydrate`
|
|
554
|
+
if (fetched && page_config.hydrate) serialized_data.push(...fetched);
|
|
555
|
+
|
|
556
|
+
if (uses_credentials) is_private = true;
|
|
557
|
+
|
|
558
|
+
maxage = loaded.maxage;
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
const session = writable($session);
|
|
562
|
+
|
|
563
|
+
/** @type {Record<string, any>} */
|
|
564
|
+
const props = {
|
|
565
|
+
stores: {
|
|
566
|
+
page: writable(null),
|
|
567
|
+
navigating: writable(null),
|
|
568
|
+
session
|
|
569
|
+
},
|
|
570
|
+
page: {
|
|
571
|
+
url: state.prerender ? create_prerendering_url_proxy(url) : url,
|
|
572
|
+
params,
|
|
573
|
+
status,
|
|
574
|
+
error,
|
|
575
|
+
stuff
|
|
576
|
+
},
|
|
577
|
+
components: branch.map(({ node }) => node.module.default)
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
// TODO remove this for 1.0
|
|
581
|
+
/**
|
|
582
|
+
* @param {string} property
|
|
583
|
+
* @param {string} replacement
|
|
584
|
+
*/
|
|
585
|
+
const print_error = (property, replacement) => {
|
|
586
|
+
Object.defineProperty(props.page, property, {
|
|
587
|
+
get: () => {
|
|
588
|
+
throw new Error(`$page.${property} has been replaced by $page.url.${replacement}`);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
print_error('origin', 'origin');
|
|
594
|
+
print_error('path', 'pathname');
|
|
595
|
+
print_error('query', 'searchParams');
|
|
596
|
+
|
|
597
|
+
// props_n (instead of props[n]) makes it easy to avoid
|
|
598
|
+
// unnecessary updates for layout components
|
|
599
|
+
for (let i = 0; i < branch.length; i += 1) {
|
|
600
|
+
props[`props_${i}`] = await branch[i].loaded.props;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
let session_tracking_active = false;
|
|
604
|
+
const unsubscribe = session.subscribe(() => {
|
|
605
|
+
if (session_tracking_active) is_private = true;
|
|
606
|
+
});
|
|
607
|
+
session_tracking_active = true;
|
|
608
|
+
|
|
609
|
+
try {
|
|
610
|
+
rendered = options.root.render(props);
|
|
611
|
+
} finally {
|
|
612
|
+
unsubscribe();
|
|
613
|
+
}
|
|
614
|
+
} else {
|
|
615
|
+
rendered = { head: '', html: '', css: { code: '', map: null } };
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
let { head, html: body } = rendered;
|
|
619
|
+
|
|
620
|
+
const inlined_style = Array.from(styles.values()).join('\n');
|
|
621
|
+
|
|
622
|
+
if (state.prerender) {
|
|
623
|
+
if (maxage) {
|
|
624
|
+
head += `<meta http-equiv="cache-control" content="max-age=${maxage}">`;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
if (options.amp) {
|
|
629
|
+
head += `
|
|
630
|
+
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style>
|
|
631
|
+
<noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
|
|
632
|
+
<script async src="https://cdn.ampproject.org/v0.js"></script>
|
|
633
|
+
|
|
634
|
+
<style amp-custom>${inlined_style}\n${rendered.css.code}</style>`;
|
|
635
|
+
|
|
636
|
+
if (options.service_worker) {
|
|
637
|
+
head +=
|
|
638
|
+
'<script async custom-element="amp-install-serviceworker" src="https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js"></script>';
|
|
639
|
+
|
|
640
|
+
body += `<amp-install-serviceworker src="${options.service_worker}" layout="nodisplay"></amp-install-serviceworker>`;
|
|
641
|
+
}
|
|
642
|
+
} else {
|
|
643
|
+
if (inlined_style) {
|
|
644
|
+
head += `\n\t<style${options.dev ? ' data-svelte' : ''}>${inlined_style}</style>`;
|
|
645
|
+
}
|
|
646
|
+
// prettier-ignore
|
|
647
|
+
head += Array.from(css)
|
|
648
|
+
.map((dep) => `\n\t<link${styles.has(dep) ? ' disabled media="(max-width: 0)"' : ''} rel="stylesheet" href="${options.prefix + dep}">`)
|
|
649
|
+
.join('');
|
|
650
|
+
|
|
651
|
+
if (page_config.router || page_config.hydrate) {
|
|
652
|
+
head += Array.from(js)
|
|
653
|
+
.map((dep) => `\n\t<link rel="modulepreload" href="${options.prefix + dep}">`)
|
|
654
|
+
.join('');
|
|
655
|
+
// prettier-ignore
|
|
656
|
+
head += `
|
|
657
|
+
<script type="module">
|
|
658
|
+
import { start } from ${s(options.prefix + options.manifest._.entry.file)};
|
|
659
|
+
start({
|
|
660
|
+
target: ${options.target ? `document.querySelector(${s(options.target)})` : 'document.body'},
|
|
661
|
+
paths: ${s(options.paths)},
|
|
662
|
+
session: ${try_serialize($session, (error) => {
|
|
663
|
+
throw new Error(`Failed to serialize session data: ${error.message}`);
|
|
664
|
+
})},
|
|
665
|
+
route: ${!!page_config.router},
|
|
666
|
+
spa: ${!ssr},
|
|
667
|
+
trailing_slash: ${s(options.trailing_slash)},
|
|
668
|
+
hydrate: ${ssr && page_config.hydrate ? `{
|
|
669
|
+
status: ${status},
|
|
670
|
+
error: ${serialize_error(error)},
|
|
671
|
+
nodes: [
|
|
672
|
+
${(branch || [])
|
|
673
|
+
.map(({ node }) => `import(${s(options.prefix + node.entry)})`)
|
|
674
|
+
.join(',\n\t\t\t\t\t\t')}
|
|
675
|
+
],
|
|
676
|
+
url: new URL(${s(url.href)}),
|
|
677
|
+
params: ${devalue(params)}
|
|
678
|
+
}` : 'null'}
|
|
679
|
+
});
|
|
680
|
+
</script>`;
|
|
681
|
+
|
|
682
|
+
body += serialized_data
|
|
683
|
+
.map(({ url, body, json }) => {
|
|
684
|
+
let attributes = `type="application/json" data-type="svelte-data" data-url=${escape_html_attr(
|
|
685
|
+
url
|
|
686
|
+
)}`;
|
|
687
|
+
if (body) attributes += ` data-body="${hash(body)}"`;
|
|
688
|
+
|
|
689
|
+
return `<script ${attributes}>${json}</script>`;
|
|
690
|
+
})
|
|
691
|
+
.join('\n\n\t');
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (options.service_worker) {
|
|
695
|
+
// always include service worker unless it's turned off explicitly
|
|
696
|
+
head += `
|
|
697
|
+
<script>
|
|
698
|
+
if ('serviceWorker' in navigator) {
|
|
699
|
+
navigator.serviceWorker.register('${options.service_worker}');
|
|
700
|
+
}
|
|
701
|
+
</script>`;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
const segments = url.pathname.slice(options.paths.base.length).split('/').slice(2);
|
|
706
|
+
const assets =
|
|
707
|
+
options.paths.assets || (segments.length > 0 ? segments.map(() => '..').join('/') : '.');
|
|
708
|
+
|
|
709
|
+
const html = options.template({ head, body, assets });
|
|
710
|
+
|
|
711
|
+
const headers = new Headers({
|
|
712
|
+
'content-type': 'text/html',
|
|
713
|
+
etag: `"${hash(html)}"`
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
if (maxage) {
|
|
717
|
+
headers.set('cache-control', `${is_private ? 'private' : 'public'}, max-age=${maxage}`);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (!options.floc) {
|
|
721
|
+
headers.set('permissions-policy', 'interest-cohort=()');
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return new Response(html, {
|
|
725
|
+
status,
|
|
726
|
+
headers
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* @param {any} data
|
|
732
|
+
* @param {(error: Error) => void} [fail]
|
|
733
|
+
*/
|
|
734
|
+
function try_serialize(data, fail) {
|
|
735
|
+
try {
|
|
736
|
+
return devalue(data);
|
|
737
|
+
} catch (err) {
|
|
738
|
+
if (fail) fail(coalesce_to_error(err));
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// Ensure we return something truthy so the client will not re-render the page over the error
|
|
744
|
+
|
|
745
|
+
/** @param {(Error & {frame?: string} & {loc?: object}) | undefined | null} error */
|
|
746
|
+
function serialize_error(error) {
|
|
747
|
+
if (!error) return null;
|
|
748
|
+
let serialized = try_serialize(error);
|
|
749
|
+
if (!serialized) {
|
|
750
|
+
const { name, message, stack } = error;
|
|
751
|
+
serialized = try_serialize({ ...error, name, message, stack });
|
|
752
|
+
}
|
|
753
|
+
if (!serialized) {
|
|
754
|
+
serialized = '{}';
|
|
755
|
+
}
|
|
756
|
+
return serialized;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* @param {import('types/page').LoadOutput} loaded
|
|
761
|
+
* @returns {import('types/internal').NormalizedLoadOutput}
|
|
762
|
+
*/
|
|
763
|
+
function normalize(loaded) {
|
|
764
|
+
const has_error_status =
|
|
765
|
+
loaded.status && loaded.status >= 400 && loaded.status <= 599 && !loaded.redirect;
|
|
766
|
+
if (loaded.error || has_error_status) {
|
|
767
|
+
const status = loaded.status;
|
|
768
|
+
|
|
769
|
+
if (!loaded.error && has_error_status) {
|
|
770
|
+
return {
|
|
771
|
+
status: status || 500,
|
|
772
|
+
error: new Error()
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const error = typeof loaded.error === 'string' ? new Error(loaded.error) : loaded.error;
|
|
777
|
+
|
|
778
|
+
if (!(error instanceof Error)) {
|
|
779
|
+
return {
|
|
780
|
+
status: 500,
|
|
781
|
+
error: new Error(
|
|
782
|
+
`"error" property returned from load() must be a string or instance of Error, received type "${typeof error}"`
|
|
783
|
+
)
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (!status || status < 400 || status > 599) {
|
|
788
|
+
console.warn('"error" returned from load() without a valid status code — defaulting to 500');
|
|
789
|
+
return { status: 500, error };
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
return { status, error };
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
if (loaded.redirect) {
|
|
796
|
+
if (!loaded.status || Math.floor(loaded.status / 100) !== 3) {
|
|
797
|
+
return {
|
|
798
|
+
status: 500,
|
|
799
|
+
error: new Error(
|
|
800
|
+
'"redirect" property returned from load() must be accompanied by a 3xx status code'
|
|
801
|
+
)
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
if (typeof loaded.redirect !== 'string') {
|
|
806
|
+
return {
|
|
807
|
+
status: 500,
|
|
808
|
+
error: new Error('"redirect" property returned from load() must be a string')
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// TODO remove before 1.0
|
|
814
|
+
if (/** @type {any} */ (loaded).context) {
|
|
815
|
+
throw new Error(
|
|
816
|
+
'You are returning "context" from a load function. ' +
|
|
817
|
+
'"context" was renamed to "stuff", please adjust your code accordingly.'
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
return /** @type {import('types/internal').NormalizedLoadOutput} */ (loaded);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* @param {{
|
|
826
|
+
* event: import('types/hooks').RequestEvent;
|
|
827
|
+
* options: import('types/internal').SSRRenderOptions;
|
|
828
|
+
* state: import('types/internal').SSRRenderState;
|
|
829
|
+
* route: import('types/internal').SSRPage | null;
|
|
830
|
+
* url: URL;
|
|
831
|
+
* params: Record<string, string>;
|
|
832
|
+
* node: import('types/internal').SSRNode;
|
|
833
|
+
* $session: any;
|
|
834
|
+
* stuff: Record<string, any>;
|
|
835
|
+
* is_error: boolean;
|
|
836
|
+
* status?: number;
|
|
837
|
+
* error?: Error;
|
|
838
|
+
* }} opts
|
|
839
|
+
* @returns {Promise<import('./types').Loaded | undefined>} undefined for fallthrough
|
|
840
|
+
*/
|
|
841
|
+
async function load_node({
|
|
842
|
+
event,
|
|
843
|
+
options,
|
|
844
|
+
state,
|
|
845
|
+
route,
|
|
846
|
+
url,
|
|
847
|
+
params,
|
|
848
|
+
node,
|
|
849
|
+
$session,
|
|
850
|
+
stuff,
|
|
851
|
+
is_error,
|
|
852
|
+
status,
|
|
853
|
+
error
|
|
854
|
+
}) {
|
|
855
|
+
const { module } = node;
|
|
856
|
+
|
|
857
|
+
let uses_credentials = false;
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* @type {Array<{
|
|
861
|
+
* url: string;
|
|
862
|
+
* body: string;
|
|
863
|
+
* json: string;
|
|
864
|
+
* }>}
|
|
865
|
+
*/
|
|
866
|
+
const fetched = [];
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* @type {string[]}
|
|
870
|
+
*/
|
|
871
|
+
let set_cookie_headers = [];
|
|
872
|
+
|
|
873
|
+
let loaded;
|
|
874
|
+
|
|
875
|
+
if (module.load) {
|
|
876
|
+
/** @type {import('types/page').LoadInput | import('types/page').ErrorLoadInput} */
|
|
877
|
+
const load_input = {
|
|
878
|
+
url: state.prerender ? create_prerendering_url_proxy(url) : url,
|
|
879
|
+
params,
|
|
880
|
+
get session() {
|
|
881
|
+
uses_credentials = true;
|
|
882
|
+
return $session;
|
|
883
|
+
},
|
|
884
|
+
/**
|
|
885
|
+
* @param {RequestInfo} resource
|
|
886
|
+
* @param {RequestInit} opts
|
|
887
|
+
*/
|
|
888
|
+
fetch: async (resource, opts = {}) => {
|
|
889
|
+
/** @type {string} */
|
|
890
|
+
let requested;
|
|
891
|
+
|
|
892
|
+
if (typeof resource === 'string') {
|
|
893
|
+
requested = resource;
|
|
894
|
+
} else {
|
|
895
|
+
requested = resource.url;
|
|
896
|
+
|
|
897
|
+
opts = {
|
|
898
|
+
method: resource.method,
|
|
899
|
+
headers: resource.headers,
|
|
900
|
+
body: resource.body,
|
|
901
|
+
mode: resource.mode,
|
|
902
|
+
credentials: resource.credentials,
|
|
903
|
+
cache: resource.cache,
|
|
904
|
+
redirect: resource.redirect,
|
|
905
|
+
referrer: resource.referrer,
|
|
906
|
+
integrity: resource.integrity,
|
|
907
|
+
...opts
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
opts.headers = new Headers(opts.headers);
|
|
912
|
+
|
|
913
|
+
const resolved = resolve(event.url.pathname, requested.split('?')[0]);
|
|
914
|
+
|
|
915
|
+
let response;
|
|
916
|
+
|
|
917
|
+
// handle fetch requests for static assets. e.g. prebaked data, etc.
|
|
918
|
+
// we need to support everything the browser's fetch supports
|
|
919
|
+
const prefix = options.paths.assets || options.paths.base;
|
|
920
|
+
const filename = (
|
|
921
|
+
resolved.startsWith(prefix) ? resolved.slice(prefix.length) : resolved
|
|
922
|
+
).slice(1);
|
|
923
|
+
const filename_html = `${filename}/index.html`; // path may also match path/index.html
|
|
924
|
+
|
|
925
|
+
const is_asset = options.manifest.assets.has(filename);
|
|
926
|
+
const is_asset_html = options.manifest.assets.has(filename_html);
|
|
927
|
+
|
|
928
|
+
if (is_asset || is_asset_html) {
|
|
929
|
+
const file = is_asset ? filename : filename_html;
|
|
930
|
+
|
|
931
|
+
if (options.read) {
|
|
932
|
+
const type = is_asset
|
|
933
|
+
? options.manifest._.mime[filename.slice(filename.lastIndexOf('.'))]
|
|
934
|
+
: 'text/html';
|
|
935
|
+
|
|
936
|
+
response = new Response(options.read(file), {
|
|
937
|
+
headers: type ? { 'content-type': type } : {}
|
|
938
|
+
});
|
|
939
|
+
} else {
|
|
940
|
+
response = await fetch(`${url.origin}/${file}`, /** @type {RequestInit} */ (opts));
|
|
941
|
+
}
|
|
942
|
+
} else if (is_root_relative(resolved)) {
|
|
943
|
+
const relative = resolved;
|
|
944
|
+
|
|
945
|
+
// TODO: fix type https://github.com/node-fetch/node-fetch/issues/1113
|
|
946
|
+
if (opts.credentials !== 'omit') {
|
|
947
|
+
uses_credentials = true;
|
|
948
|
+
|
|
949
|
+
const cookie = event.request.headers.get('cookie');
|
|
950
|
+
const authorization = event.request.headers.get('authorization');
|
|
951
|
+
|
|
952
|
+
if (cookie) {
|
|
953
|
+
opts.headers.set('cookie', cookie);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
if (authorization && !opts.headers.has('authorization')) {
|
|
957
|
+
opts.headers.set('authorization', authorization);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
if (opts.body && typeof opts.body !== 'string') {
|
|
962
|
+
// per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a
|
|
963
|
+
// Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object.
|
|
964
|
+
// non-string bodies are irksome to deal with, but luckily aren't particularly useful
|
|
965
|
+
// in this context anyway, so we take the easy route and ban them
|
|
966
|
+
throw new Error('Request body must be a string');
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
const rendered = await respond(
|
|
970
|
+
new Request(new URL(requested, event.url).href, opts),
|
|
971
|
+
options,
|
|
972
|
+
{
|
|
973
|
+
fetched: requested,
|
|
974
|
+
initiator: route
|
|
975
|
+
}
|
|
976
|
+
);
|
|
977
|
+
|
|
978
|
+
if (rendered) {
|
|
979
|
+
if (state.prerender) {
|
|
980
|
+
state.prerender.dependencies.set(relative, rendered);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
response = rendered;
|
|
984
|
+
} else {
|
|
985
|
+
// we can't load the endpoint from our own manifest,
|
|
986
|
+
// so we need to make an actual HTTP request
|
|
987
|
+
return fetch(new URL(requested, event.url).href, {
|
|
988
|
+
method: opts.method || 'GET',
|
|
989
|
+
headers: opts.headers
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
} else {
|
|
993
|
+
// external
|
|
994
|
+
if (resolved.startsWith('//')) {
|
|
995
|
+
throw new Error(
|
|
996
|
+
`Cannot request protocol-relative URL (${requested}) in server-side fetch`
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// external fetch
|
|
1001
|
+
// allow cookie passthrough for "same-origin"
|
|
1002
|
+
// if SvelteKit is serving my.domain.com:
|
|
1003
|
+
// - domain.com WILL NOT receive cookies
|
|
1004
|
+
// - my.domain.com WILL receive cookies
|
|
1005
|
+
// - api.domain.dom WILL NOT receive cookies
|
|
1006
|
+
// - sub.my.domain.com WILL receive cookies
|
|
1007
|
+
// ports do not affect the resolution
|
|
1008
|
+
// leading dot prevents mydomain.com matching domain.com
|
|
1009
|
+
if (
|
|
1010
|
+
`.${new URL(requested).hostname}`.endsWith(`.${event.url.hostname}`) &&
|
|
1011
|
+
opts.credentials !== 'omit'
|
|
1012
|
+
) {
|
|
1013
|
+
uses_credentials = true;
|
|
1014
|
+
|
|
1015
|
+
const cookie = event.request.headers.get('cookie');
|
|
1016
|
+
if (cookie) opts.headers.set('cookie', cookie);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
const external_request = new Request(requested, /** @type {RequestInit} */ (opts));
|
|
1020
|
+
response = await options.hooks.externalFetch.call(null, external_request);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
if (response) {
|
|
1024
|
+
const proxy = new Proxy(response, {
|
|
1025
|
+
get(response, key, _receiver) {
|
|
1026
|
+
async function text() {
|
|
1027
|
+
const body = await response.text();
|
|
1028
|
+
|
|
1029
|
+
/** @type {import('types/helper').ResponseHeaders} */
|
|
1030
|
+
const headers = {};
|
|
1031
|
+
for (const [key, value] of response.headers) {
|
|
1032
|
+
if (key === 'set-cookie') {
|
|
1033
|
+
set_cookie_headers = set_cookie_headers.concat(value);
|
|
1034
|
+
} else if (key !== 'etag') {
|
|
1035
|
+
headers[key] = value;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
if (!opts.body || typeof opts.body === 'string') {
|
|
1040
|
+
// prettier-ignore
|
|
1041
|
+
fetched.push({
|
|
1042
|
+
url: requested,
|
|
1043
|
+
body: /** @type {string} */ (opts.body),
|
|
1044
|
+
json: `{"status":${response.status},"statusText":${s(response.statusText)},"headers":${s(headers)},"body":"${escape_json_string_in_html(body)}"}`
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
return body;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
if (key === 'text') {
|
|
1052
|
+
return text;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
if (key === 'json') {
|
|
1056
|
+
return async () => {
|
|
1057
|
+
return JSON.parse(await text());
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// TODO arrayBuffer?
|
|
1062
|
+
|
|
1063
|
+
return Reflect.get(response, key, response);
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
return proxy;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
return (
|
|
1071
|
+
response ||
|
|
1072
|
+
new Response('Not found', {
|
|
1073
|
+
status: 404
|
|
1074
|
+
})
|
|
1075
|
+
);
|
|
1076
|
+
},
|
|
1077
|
+
stuff: { ...stuff }
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
if (options.dev) {
|
|
1081
|
+
// TODO remove this for 1.0
|
|
1082
|
+
Object.defineProperty(load_input, 'page', {
|
|
1083
|
+
get: () => {
|
|
1084
|
+
throw new Error('`page` in `load` functions has been replaced by `url` and `params`');
|
|
1085
|
+
}
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
if (is_error) {
|
|
1090
|
+
/** @type {import('types/page').ErrorLoadInput} */ (load_input).status = status;
|
|
1091
|
+
/** @type {import('types/page').ErrorLoadInput} */ (load_input).error = error;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
loaded = await module.load.call(null, load_input);
|
|
1095
|
+
|
|
1096
|
+
if (!loaded) {
|
|
1097
|
+
throw new Error(`load function must return a value${options.dev ? ` (${node.entry})` : ''}`);
|
|
1098
|
+
}
|
|
1099
|
+
} else {
|
|
1100
|
+
loaded = {};
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
if (loaded.fallthrough && !is_error) {
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
return {
|
|
1108
|
+
node,
|
|
1109
|
+
loaded: normalize(loaded),
|
|
1110
|
+
stuff: loaded.stuff || stuff,
|
|
1111
|
+
fetched,
|
|
1112
|
+
set_cookie_headers,
|
|
1113
|
+
uses_credentials
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* @typedef {import('./types.js').Loaded} Loaded
|
|
1119
|
+
* @typedef {import('types/internal').SSRRenderOptions} SSRRenderOptions
|
|
1120
|
+
* @typedef {import('types/internal').SSRRenderState} SSRRenderState
|
|
1121
|
+
*/
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* @param {{
|
|
1125
|
+
* event: import('types/hooks').RequestEvent;
|
|
1126
|
+
* options: SSRRenderOptions;
|
|
1127
|
+
* state: SSRRenderState;
|
|
1128
|
+
* $session: any;
|
|
1129
|
+
* status: number;
|
|
1130
|
+
* error: Error;
|
|
1131
|
+
* ssr: boolean;
|
|
1132
|
+
* }} opts
|
|
1133
|
+
*/
|
|
1134
|
+
async function respond_with_error({ event, options, state, $session, status, error, ssr }) {
|
|
1135
|
+
try {
|
|
1136
|
+
const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout
|
|
1137
|
+
const default_error = await options.manifest._.nodes[1](); // 1 is always the root error
|
|
1138
|
+
|
|
1139
|
+
/** @type {Record<string, string>} */
|
|
1140
|
+
const params = {}; // error page has no params
|
|
1141
|
+
|
|
1142
|
+
const layout_loaded = /** @type {Loaded} */ (
|
|
1143
|
+
await load_node({
|
|
1144
|
+
event,
|
|
1145
|
+
options,
|
|
1146
|
+
state,
|
|
1147
|
+
route: null,
|
|
1148
|
+
url: event.url, // TODO this is redundant, no?
|
|
1149
|
+
params,
|
|
1150
|
+
node: default_layout,
|
|
1151
|
+
$session,
|
|
1152
|
+
stuff: {},
|
|
1153
|
+
is_error: false
|
|
1154
|
+
})
|
|
1155
|
+
);
|
|
1156
|
+
|
|
1157
|
+
const error_loaded = /** @type {Loaded} */ (
|
|
1158
|
+
await load_node({
|
|
1159
|
+
event,
|
|
1160
|
+
options,
|
|
1161
|
+
state,
|
|
1162
|
+
route: null,
|
|
1163
|
+
url: event.url,
|
|
1164
|
+
params,
|
|
1165
|
+
node: default_error,
|
|
1166
|
+
$session,
|
|
1167
|
+
stuff: layout_loaded ? layout_loaded.stuff : {},
|
|
1168
|
+
is_error: true,
|
|
1169
|
+
status,
|
|
1170
|
+
error
|
|
1171
|
+
})
|
|
1172
|
+
);
|
|
1173
|
+
|
|
1174
|
+
return await render_response({
|
|
1175
|
+
options,
|
|
1176
|
+
state,
|
|
1177
|
+
$session,
|
|
1178
|
+
page_config: {
|
|
1179
|
+
hydrate: options.hydrate,
|
|
1180
|
+
router: options.router
|
|
1181
|
+
},
|
|
1182
|
+
stuff: error_loaded.stuff,
|
|
1183
|
+
status,
|
|
1184
|
+
error,
|
|
1185
|
+
branch: [layout_loaded, error_loaded],
|
|
1186
|
+
url: event.url,
|
|
1187
|
+
params,
|
|
1188
|
+
ssr
|
|
1189
|
+
});
|
|
1190
|
+
} catch (err) {
|
|
1191
|
+
const error = coalesce_to_error(err);
|
|
1192
|
+
|
|
1193
|
+
options.handle_error(error, event);
|
|
1194
|
+
|
|
1195
|
+
return new Response(error.stack, {
|
|
1196
|
+
status: 500
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* @typedef {import('./types.js').Loaded} Loaded
|
|
1203
|
+
* @typedef {import('types/internal').SSRNode} SSRNode
|
|
1204
|
+
* @typedef {import('types/internal').SSRRenderOptions} SSRRenderOptions
|
|
1205
|
+
* @typedef {import('types/internal').SSRRenderState} SSRRenderState
|
|
1206
|
+
*/
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
* @param {{
|
|
1210
|
+
* event: import('types/hooks').RequestEvent;
|
|
1211
|
+
* options: SSRRenderOptions;
|
|
1212
|
+
* state: SSRRenderState;
|
|
1213
|
+
* $session: any;
|
|
1214
|
+
* route: import('types/internal').SSRPage;
|
|
1215
|
+
* params: Record<string, string>;
|
|
1216
|
+
* ssr: boolean;
|
|
1217
|
+
* }} opts
|
|
1218
|
+
* @returns {Promise<Response | undefined>}
|
|
1219
|
+
*/
|
|
1220
|
+
async function respond$1(opts) {
|
|
1221
|
+
const { event, options, state, $session, route, ssr } = opts;
|
|
1222
|
+
|
|
1223
|
+
/** @type {Array<SSRNode | undefined>} */
|
|
1224
|
+
let nodes;
|
|
1225
|
+
|
|
1226
|
+
if (!ssr) {
|
|
1227
|
+
return await render_response({
|
|
1228
|
+
...opts,
|
|
1229
|
+
branch: [],
|
|
1230
|
+
page_config: {
|
|
1231
|
+
hydrate: true,
|
|
1232
|
+
router: true
|
|
1233
|
+
},
|
|
1234
|
+
status: 200,
|
|
1235
|
+
url: event.url,
|
|
1236
|
+
stuff: {}
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
try {
|
|
1241
|
+
nodes = await Promise.all(
|
|
1242
|
+
route.a.map((n) => options.manifest._.nodes[n] && options.manifest._.nodes[n]())
|
|
1243
|
+
);
|
|
1244
|
+
} catch (err) {
|
|
1245
|
+
const error = coalesce_to_error(err);
|
|
1246
|
+
|
|
1247
|
+
options.handle_error(error, event);
|
|
1248
|
+
|
|
1249
|
+
return await respond_with_error({
|
|
1250
|
+
event,
|
|
1251
|
+
options,
|
|
1252
|
+
state,
|
|
1253
|
+
$session,
|
|
1254
|
+
status: 500,
|
|
1255
|
+
error,
|
|
1256
|
+
ssr
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// the leaf node will be present. only layouts may be undefined
|
|
1261
|
+
const leaf = /** @type {SSRNode} */ (nodes[nodes.length - 1]).module;
|
|
1262
|
+
|
|
1263
|
+
let page_config = get_page_config(leaf, options);
|
|
1264
|
+
|
|
1265
|
+
if (!leaf.prerender && state.prerender && !state.prerender.all) {
|
|
1266
|
+
// if the page has `export const prerender = true`, continue,
|
|
1267
|
+
// otherwise bail out at this point
|
|
1268
|
+
return new Response(undefined, {
|
|
1269
|
+
status: 204
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
/** @type {Array<Loaded>} */
|
|
1274
|
+
let branch = [];
|
|
1275
|
+
|
|
1276
|
+
/** @type {number} */
|
|
1277
|
+
let status = 200;
|
|
1278
|
+
|
|
1279
|
+
/** @type {Error|undefined} */
|
|
1280
|
+
let error;
|
|
1281
|
+
|
|
1282
|
+
/** @type {string[]} */
|
|
1283
|
+
let set_cookie_headers = [];
|
|
1284
|
+
|
|
1285
|
+
let stuff = {};
|
|
1286
|
+
|
|
1287
|
+
ssr: if (ssr) {
|
|
1288
|
+
for (let i = 0; i < nodes.length; i += 1) {
|
|
1289
|
+
const node = nodes[i];
|
|
1290
|
+
|
|
1291
|
+
/** @type {Loaded | undefined} */
|
|
1292
|
+
let loaded;
|
|
1293
|
+
|
|
1294
|
+
if (node) {
|
|
1295
|
+
try {
|
|
1296
|
+
loaded = await load_node({
|
|
1297
|
+
...opts,
|
|
1298
|
+
url: event.url,
|
|
1299
|
+
node,
|
|
1300
|
+
stuff,
|
|
1301
|
+
is_error: false
|
|
1302
|
+
});
|
|
1303
|
+
|
|
1304
|
+
if (!loaded) return;
|
|
1305
|
+
|
|
1306
|
+
set_cookie_headers = set_cookie_headers.concat(loaded.set_cookie_headers);
|
|
1307
|
+
|
|
1308
|
+
if (loaded.loaded.redirect) {
|
|
1309
|
+
return with_cookies(
|
|
1310
|
+
new Response(undefined, {
|
|
1311
|
+
status: loaded.loaded.status,
|
|
1312
|
+
headers: {
|
|
1313
|
+
location: loaded.loaded.redirect
|
|
1314
|
+
}
|
|
1315
|
+
}),
|
|
1316
|
+
set_cookie_headers
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
if (loaded.loaded.error) {
|
|
1321
|
+
({ status, error } = loaded.loaded);
|
|
1322
|
+
}
|
|
1323
|
+
} catch (err) {
|
|
1324
|
+
const e = coalesce_to_error(err);
|
|
1325
|
+
|
|
1326
|
+
options.handle_error(e, event);
|
|
1327
|
+
|
|
1328
|
+
status = 500;
|
|
1329
|
+
error = e;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
if (loaded && !error) {
|
|
1333
|
+
branch.push(loaded);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
if (error) {
|
|
1337
|
+
while (i--) {
|
|
1338
|
+
if (route.b[i]) {
|
|
1339
|
+
const error_node = await options.manifest._.nodes[route.b[i]]();
|
|
1340
|
+
|
|
1341
|
+
/** @type {Loaded} */
|
|
1342
|
+
let node_loaded;
|
|
1343
|
+
let j = i;
|
|
1344
|
+
while (!(node_loaded = branch[j])) {
|
|
1345
|
+
j -= 1;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
try {
|
|
1349
|
+
const error_loaded = /** @type {import('./types').Loaded} */ (
|
|
1350
|
+
await load_node({
|
|
1351
|
+
...opts,
|
|
1352
|
+
url: event.url,
|
|
1353
|
+
node: error_node,
|
|
1354
|
+
stuff: node_loaded.stuff,
|
|
1355
|
+
is_error: true,
|
|
1356
|
+
status,
|
|
1357
|
+
error
|
|
1358
|
+
})
|
|
1359
|
+
);
|
|
1360
|
+
|
|
1361
|
+
if (error_loaded.loaded.error) {
|
|
1362
|
+
continue;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
page_config = get_page_config(error_node.module, options);
|
|
1366
|
+
branch = branch.slice(0, j + 1).concat(error_loaded);
|
|
1367
|
+
stuff = { ...node_loaded.stuff, ...error_loaded.stuff };
|
|
1368
|
+
break ssr;
|
|
1369
|
+
} catch (err) {
|
|
1370
|
+
const e = coalesce_to_error(err);
|
|
1371
|
+
|
|
1372
|
+
options.handle_error(e, event);
|
|
1373
|
+
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// TODO backtrack until we find an __error.svelte component
|
|
1380
|
+
// that we can use as the leaf node
|
|
1381
|
+
// for now just return regular error page
|
|
1382
|
+
return with_cookies(
|
|
1383
|
+
await respond_with_error({
|
|
1384
|
+
event,
|
|
1385
|
+
options,
|
|
1386
|
+
state,
|
|
1387
|
+
$session,
|
|
1388
|
+
status,
|
|
1389
|
+
error,
|
|
1390
|
+
ssr
|
|
1391
|
+
}),
|
|
1392
|
+
set_cookie_headers
|
|
1393
|
+
);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
if (loaded && loaded.loaded.stuff) {
|
|
1398
|
+
stuff = {
|
|
1399
|
+
...stuff,
|
|
1400
|
+
...loaded.loaded.stuff
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
try {
|
|
1407
|
+
return with_cookies(
|
|
1408
|
+
await render_response({
|
|
1409
|
+
...opts,
|
|
1410
|
+
stuff,
|
|
1411
|
+
url: event.url,
|
|
1412
|
+
page_config,
|
|
1413
|
+
status,
|
|
1414
|
+
error,
|
|
1415
|
+
branch: branch.filter(Boolean)
|
|
1416
|
+
}),
|
|
1417
|
+
set_cookie_headers
|
|
1418
|
+
);
|
|
1419
|
+
} catch (err) {
|
|
1420
|
+
const error = coalesce_to_error(err);
|
|
1421
|
+
|
|
1422
|
+
options.handle_error(error, event);
|
|
1423
|
+
|
|
1424
|
+
return with_cookies(
|
|
1425
|
+
await respond_with_error({
|
|
1426
|
+
...opts,
|
|
1427
|
+
status: 500,
|
|
1428
|
+
error
|
|
1429
|
+
}),
|
|
1430
|
+
set_cookie_headers
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
/**
|
|
1436
|
+
* @param {import('types/internal').SSRComponent} leaf
|
|
1437
|
+
* @param {SSRRenderOptions} options
|
|
1438
|
+
*/
|
|
1439
|
+
function get_page_config(leaf, options) {
|
|
1440
|
+
// TODO remove for 1.0
|
|
1441
|
+
if ('ssr' in leaf) {
|
|
1442
|
+
throw new Error(
|
|
1443
|
+
'`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs#hooks-handle'
|
|
1444
|
+
);
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
return {
|
|
1448
|
+
router: 'router' in leaf ? !!leaf.router : options.router,
|
|
1449
|
+
hydrate: 'hydrate' in leaf ? !!leaf.hydrate : options.hydrate
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
/**
|
|
1454
|
+
* @param {Response} response
|
|
1455
|
+
* @param {string[]} set_cookie_headers
|
|
1456
|
+
*/
|
|
1457
|
+
function with_cookies(response, set_cookie_headers) {
|
|
1458
|
+
if (set_cookie_headers.length) {
|
|
1459
|
+
set_cookie_headers.forEach((value) => {
|
|
1460
|
+
response.headers.append('set-cookie', value);
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
return response;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
/**
|
|
1467
|
+
* @param {import('types/hooks').RequestEvent} event
|
|
1468
|
+
* @param {import('types/internal').SSRPage} route
|
|
1469
|
+
* @param {RegExpExecArray} match
|
|
1470
|
+
* @param {import('types/internal').SSRRenderOptions} options
|
|
1471
|
+
* @param {import('types/internal').SSRRenderState} state
|
|
1472
|
+
* @param {boolean} ssr
|
|
1473
|
+
* @returns {Promise<Response | undefined>}
|
|
1474
|
+
*/
|
|
1475
|
+
async function render_page(event, route, match, options, state, ssr) {
|
|
1476
|
+
if (state.initiator === route) {
|
|
1477
|
+
// infinite request cycle detected
|
|
1478
|
+
return new Response(`Not found: ${event.url.pathname}`, {
|
|
1479
|
+
status: 404
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
const params = route.params ? decode_params(route.params(match)) : {};
|
|
1484
|
+
|
|
1485
|
+
const $session = await options.hooks.getSession(event);
|
|
1486
|
+
|
|
1487
|
+
const response = await respond$1({
|
|
1488
|
+
event,
|
|
1489
|
+
options,
|
|
1490
|
+
state,
|
|
1491
|
+
$session,
|
|
1492
|
+
route,
|
|
1493
|
+
params,
|
|
1494
|
+
ssr
|
|
1495
|
+
});
|
|
1496
|
+
|
|
1497
|
+
if (response) {
|
|
1498
|
+
return response;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
if (state.fetched) {
|
|
1502
|
+
// we came here because of a bad request in a `load` function.
|
|
1503
|
+
// rather than render the error page — which could lead to an
|
|
1504
|
+
// infinite loop, if the `load` belonged to the root layout,
|
|
1505
|
+
// we respond with a bare-bones 500
|
|
1506
|
+
return new Response(`Bad request in load function: failed to fetch ${state.fetched}`, {
|
|
1507
|
+
status: 500
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
/** @type {import('types/internal').Respond} */
|
|
1513
|
+
async function respond(request, options, state = {}) {
|
|
1514
|
+
const url = new URL(request.url);
|
|
1515
|
+
|
|
1516
|
+
if (url.pathname !== '/' && options.trailing_slash !== 'ignore') {
|
|
1517
|
+
const has_trailing_slash = url.pathname.endsWith('/');
|
|
1518
|
+
|
|
1519
|
+
if (
|
|
1520
|
+
(has_trailing_slash && options.trailing_slash === 'never') ||
|
|
1521
|
+
(!has_trailing_slash &&
|
|
1522
|
+
options.trailing_slash === 'always' &&
|
|
1523
|
+
!(url.pathname.split('/').pop() || '').includes('.'))
|
|
1524
|
+
) {
|
|
1525
|
+
url.pathname = has_trailing_slash ? url.pathname.slice(0, -1) : url.pathname + '/';
|
|
1526
|
+
|
|
1527
|
+
if (url.search === '?') url.search = '';
|
|
1528
|
+
|
|
1529
|
+
return new Response(undefined, {
|
|
1530
|
+
status: 301,
|
|
1531
|
+
headers: {
|
|
1532
|
+
location: url.pathname + url.search
|
|
1533
|
+
}
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
const { parameter, allowed } = options.method_override;
|
|
1539
|
+
const method_override = url.searchParams.get(parameter)?.toUpperCase();
|
|
1540
|
+
|
|
1541
|
+
if (method_override) {
|
|
1542
|
+
if (request.method === 'POST') {
|
|
1543
|
+
if (allowed.includes(method_override)) {
|
|
1544
|
+
request = new Proxy(request, {
|
|
1545
|
+
get: (target, property, _receiver) => {
|
|
1546
|
+
if (property === 'method') return method_override;
|
|
1547
|
+
return Reflect.get(target, property, target);
|
|
1548
|
+
}
|
|
1549
|
+
});
|
|
1550
|
+
} else {
|
|
1551
|
+
const verb = allowed.length === 0 ? 'enabled' : 'allowed';
|
|
1552
|
+
const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs#configuration-methodoverride`;
|
|
1553
|
+
|
|
1554
|
+
return new Response(body, {
|
|
1555
|
+
status: 400
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
} else {
|
|
1559
|
+
throw new Error(`${parameter}=${method_override} is only allowed with POST requests`);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
/** @type {import('types/hooks').RequestEvent} */
|
|
1564
|
+
const event = {
|
|
1565
|
+
request,
|
|
1566
|
+
url,
|
|
1567
|
+
params: {},
|
|
1568
|
+
locals: {}
|
|
1569
|
+
};
|
|
1570
|
+
|
|
1571
|
+
// TODO remove this for 1.0
|
|
1572
|
+
/**
|
|
1573
|
+
* @param {string} property
|
|
1574
|
+
* @param {string} replacement
|
|
1575
|
+
* @param {string} suffix
|
|
1576
|
+
*/
|
|
1577
|
+
const removed = (property, replacement, suffix = '') => ({
|
|
1578
|
+
get: () => {
|
|
1579
|
+
throw new Error(`event.${property} has been replaced by event.${replacement}` + suffix);
|
|
1580
|
+
}
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
const details = '. See https://github.com/sveltejs/kit/pull/3384 for details';
|
|
1584
|
+
|
|
1585
|
+
const body_getter = {
|
|
1586
|
+
get: () => {
|
|
1587
|
+
throw new Error(
|
|
1588
|
+
'To access the request body use the text/json/arrayBuffer/formData methods, e.g. `body = await request.json()`' +
|
|
1589
|
+
details
|
|
1590
|
+
);
|
|
1591
|
+
}
|
|
1592
|
+
};
|
|
1593
|
+
|
|
1594
|
+
Object.defineProperties(event, {
|
|
1595
|
+
method: removed('method', 'request.method', details),
|
|
1596
|
+
headers: removed('headers', 'request.headers', details),
|
|
1597
|
+
origin: removed('origin', 'url.origin'),
|
|
1598
|
+
path: removed('path', 'url.pathname'),
|
|
1599
|
+
query: removed('query', 'url.searchParams'),
|
|
1600
|
+
body: body_getter,
|
|
1601
|
+
rawBody: body_getter
|
|
1602
|
+
});
|
|
1603
|
+
|
|
1604
|
+
let ssr = true;
|
|
1605
|
+
|
|
1606
|
+
try {
|
|
1607
|
+
const response = await options.hooks.handle({
|
|
1608
|
+
event,
|
|
1609
|
+
resolve: async (event, opts) => {
|
|
1610
|
+
if (opts && 'ssr' in opts) ssr = /** @type {boolean} */ (opts.ssr);
|
|
1611
|
+
|
|
1612
|
+
if (state.prerender && state.prerender.fallback) {
|
|
1613
|
+
return await render_response({
|
|
1614
|
+
url: event.url,
|
|
1615
|
+
params: event.params,
|
|
1616
|
+
options,
|
|
1617
|
+
state,
|
|
1618
|
+
$session: await options.hooks.getSession(event),
|
|
1619
|
+
page_config: { router: true, hydrate: true },
|
|
1620
|
+
stuff: {},
|
|
1621
|
+
status: 200,
|
|
1622
|
+
branch: [],
|
|
1623
|
+
ssr: false
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
let decoded = decodeURI(event.url.pathname);
|
|
1628
|
+
|
|
1629
|
+
if (options.paths.base) {
|
|
1630
|
+
if (!decoded.startsWith(options.paths.base)) return;
|
|
1631
|
+
decoded = decoded.slice(options.paths.base.length) || '/';
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
for (const route of options.manifest._.routes) {
|
|
1635
|
+
const match = route.pattern.exec(decoded);
|
|
1636
|
+
if (!match) continue;
|
|
1637
|
+
|
|
1638
|
+
const response =
|
|
1639
|
+
route.type === 'endpoint'
|
|
1640
|
+
? await render_endpoint(event, route, match)
|
|
1641
|
+
: await render_page(event, route, match, options, state, ssr);
|
|
1642
|
+
|
|
1643
|
+
if (response) {
|
|
1644
|
+
// respond with 304 if etag matches
|
|
1645
|
+
if (response.status === 200 && response.headers.has('etag')) {
|
|
1646
|
+
let if_none_match_value = request.headers.get('if-none-match');
|
|
1647
|
+
|
|
1648
|
+
// ignore W/ prefix https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match#directives
|
|
1649
|
+
if (if_none_match_value?.startsWith('W/"')) {
|
|
1650
|
+
if_none_match_value = if_none_match_value.substring(2);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
const etag = /** @type {string} */ (response.headers.get('etag'));
|
|
1654
|
+
|
|
1655
|
+
if (if_none_match_value === etag) {
|
|
1656
|
+
const headers = new Headers({ etag });
|
|
1657
|
+
|
|
1658
|
+
// https://datatracker.ietf.org/doc/html/rfc7232#section-4.1
|
|
1659
|
+
for (const key of [
|
|
1660
|
+
'cache-control',
|
|
1661
|
+
'content-location',
|
|
1662
|
+
'date',
|
|
1663
|
+
'expires',
|
|
1664
|
+
'vary'
|
|
1665
|
+
]) {
|
|
1666
|
+
const value = response.headers.get(key);
|
|
1667
|
+
if (value) headers.set(key, value);
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
return new Response(undefined, {
|
|
1671
|
+
status: 304,
|
|
1672
|
+
headers
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
return response;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
// if this request came direct from the user, rather than
|
|
1682
|
+
// via a `fetch` in a `load`, render a 404 page
|
|
1683
|
+
if (!state.initiator) {
|
|
1684
|
+
const $session = await options.hooks.getSession(event);
|
|
1685
|
+
return await respond_with_error({
|
|
1686
|
+
event,
|
|
1687
|
+
options,
|
|
1688
|
+
state,
|
|
1689
|
+
$session,
|
|
1690
|
+
status: 404,
|
|
1691
|
+
error: new Error(`Not found: ${event.url.pathname}`),
|
|
1692
|
+
ssr
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
},
|
|
1696
|
+
|
|
1697
|
+
// TODO remove for 1.0
|
|
1698
|
+
// @ts-expect-error
|
|
1699
|
+
get request() {
|
|
1700
|
+
throw new Error('request in handle has been replaced with event' + details);
|
|
1701
|
+
}
|
|
1702
|
+
});
|
|
1703
|
+
|
|
1704
|
+
// TODO for 1.0, change the error message to point to docs rather than PR
|
|
1705
|
+
if (response && !(response instanceof Response)) {
|
|
1706
|
+
throw new Error('handle must return a Response object' + details);
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
return response;
|
|
1710
|
+
} catch (/** @type {unknown} */ e) {
|
|
1711
|
+
const error = coalesce_to_error(e);
|
|
1712
|
+
|
|
1713
|
+
options.handle_error(error, event);
|
|
1714
|
+
|
|
1715
|
+
try {
|
|
1716
|
+
const $session = await options.hooks.getSession(event);
|
|
1717
|
+
return await respond_with_error({
|
|
1718
|
+
event,
|
|
1719
|
+
options,
|
|
1720
|
+
state,
|
|
1721
|
+
$session,
|
|
1722
|
+
status: 500,
|
|
1723
|
+
error,
|
|
1724
|
+
ssr
|
|
1725
|
+
});
|
|
1726
|
+
} catch (/** @type {unknown} */ e) {
|
|
1727
|
+
const error = coalesce_to_error(e);
|
|
1728
|
+
|
|
1729
|
+
return new Response(options.dev ? error.stack : error.message, {
|
|
1730
|
+
status: 500
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
/**
|
|
1737
|
+
* @param {import('types/config').ValidatedConfig} config
|
|
1738
|
+
* @param {string} cwd
|
|
1739
|
+
* @returns {Promise<import('vite').Plugin>}
|
|
1740
|
+
*/
|
|
1741
|
+
async function create_plugin(config, cwd) {
|
|
1742
|
+
/** @type {import('amphtml-validator').Validator} */
|
|
1743
|
+
let amp;
|
|
1744
|
+
|
|
1745
|
+
if (config.kit.amp) {
|
|
1746
|
+
process.env.VITE_SVELTEKIT_AMP = 'true';
|
|
1747
|
+
amp = await (await import('./index8.js').then(function (n) { return n.i; })).getInstance();
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
return {
|
|
1751
|
+
name: 'vite-plugin-svelte-kit',
|
|
1752
|
+
|
|
1753
|
+
configureServer(vite) {
|
|
1754
|
+
__fetch_polyfill();
|
|
1755
|
+
|
|
1756
|
+
/** @type {import('types/app').SSRManifest} */
|
|
1757
|
+
let manifest;
|
|
1758
|
+
|
|
1759
|
+
function update_manifest() {
|
|
1760
|
+
const manifest_data = create_manifest_data({ config, cwd });
|
|
1761
|
+
|
|
1762
|
+
create_app({ manifest_data, output: `${SVELTE_KIT}/generated`, cwd });
|
|
1763
|
+
|
|
1764
|
+
manifest = {
|
|
1765
|
+
appDir: config.kit.appDir,
|
|
1766
|
+
assets: new Set(manifest_data.assets.map((asset) => asset.file)),
|
|
1767
|
+
_: {
|
|
1768
|
+
mime: get_mime_lookup(manifest_data),
|
|
1769
|
+
entry: {
|
|
1770
|
+
file: `/@fs${runtime}/client/start.js`,
|
|
1771
|
+
css: [],
|
|
1772
|
+
js: []
|
|
1773
|
+
},
|
|
1774
|
+
nodes: manifest_data.components.map((id) => {
|
|
1775
|
+
return async () => {
|
|
1776
|
+
const url = id.startsWith('..') ? `/@fs${path__default.posix.resolve(id)}` : `/${id}`;
|
|
1777
|
+
|
|
1778
|
+
const module = /** @type {import('types/internal').SSRComponent} */ (
|
|
1779
|
+
await vite.ssrLoadModule(url)
|
|
1780
|
+
);
|
|
1781
|
+
const node = await vite.moduleGraph.getModuleByUrl(url);
|
|
1782
|
+
|
|
1783
|
+
if (!node) throw new Error(`Could not find node for ${url}`);
|
|
1784
|
+
|
|
1785
|
+
const deps = new Set();
|
|
1786
|
+
find_deps(node, deps);
|
|
1787
|
+
|
|
1788
|
+
/** @type {Record<string, string>} */
|
|
1789
|
+
const styles = {};
|
|
1790
|
+
|
|
1791
|
+
for (const dep of deps) {
|
|
1792
|
+
const parsed = new URL$1(dep.url, 'http://localhost/');
|
|
1793
|
+
const query = parsed.searchParams;
|
|
1794
|
+
|
|
1795
|
+
// TODO what about .scss files, etc?
|
|
1796
|
+
if (
|
|
1797
|
+
dep.file.endsWith('.css') ||
|
|
1798
|
+
(query.has('svelte') && query.get('type') === 'style')
|
|
1799
|
+
) {
|
|
1800
|
+
try {
|
|
1801
|
+
const mod = await vite.ssrLoadModule(dep.url);
|
|
1802
|
+
styles[dep.url] = mod.default;
|
|
1803
|
+
} catch {
|
|
1804
|
+
// this can happen with dynamically imported modules, I think
|
|
1805
|
+
// because the Vite module graph doesn't distinguish between
|
|
1806
|
+
// static and dynamic imports? TODO investigate, submit fix
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
return {
|
|
1812
|
+
module,
|
|
1813
|
+
entry: url.endsWith('.svelte') ? url : url + '?import',
|
|
1814
|
+
css: [],
|
|
1815
|
+
js: [],
|
|
1816
|
+
styles
|
|
1817
|
+
};
|
|
1818
|
+
};
|
|
1819
|
+
}),
|
|
1820
|
+
routes: manifest_data.routes.map((route) => {
|
|
1821
|
+
if (route.type === 'page') {
|
|
1822
|
+
return {
|
|
1823
|
+
type: 'page',
|
|
1824
|
+
pattern: route.pattern,
|
|
1825
|
+
params: get_params(route.params),
|
|
1826
|
+
a: route.a.map((id) => manifest_data.components.indexOf(id)),
|
|
1827
|
+
b: route.b.map((id) => manifest_data.components.indexOf(id))
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
return {
|
|
1832
|
+
type: 'endpoint',
|
|
1833
|
+
pattern: route.pattern,
|
|
1834
|
+
params: get_params(route.params),
|
|
1835
|
+
load: async () => {
|
|
1836
|
+
const url = path__default.resolve(cwd, route.file);
|
|
1837
|
+
return await vite.ssrLoadModule(url);
|
|
1838
|
+
}
|
|
1839
|
+
};
|
|
1840
|
+
})
|
|
1841
|
+
}
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
update_manifest();
|
|
1846
|
+
|
|
1847
|
+
vite.watcher.on('add', update_manifest);
|
|
1848
|
+
vite.watcher.on('remove', update_manifest);
|
|
1849
|
+
|
|
1850
|
+
const assets = config.kit.paths.assets ? SVELTE_KIT_ASSETS : config.kit.paths.base;
|
|
1851
|
+
const asset_server = sirv(config.kit.files.assets, {
|
|
1852
|
+
dev: true,
|
|
1853
|
+
etag: true,
|
|
1854
|
+
maxAge: 0,
|
|
1855
|
+
extensions: []
|
|
1856
|
+
});
|
|
1857
|
+
|
|
1858
|
+
return () => {
|
|
1859
|
+
remove_html_middlewares(vite.middlewares);
|
|
1860
|
+
|
|
1861
|
+
vite.middlewares.use(async (req, res) => {
|
|
1862
|
+
try {
|
|
1863
|
+
if (!req.url || !req.method) throw new Error('Incomplete request');
|
|
1864
|
+
if (req.url === '/favicon.ico') return not_found(res);
|
|
1865
|
+
|
|
1866
|
+
const base = `${vite.config.server.https ? 'https' : 'http'}://${req.headers.host}`;
|
|
1867
|
+
|
|
1868
|
+
const decoded = decodeURI(new URL$1(base + req.url).pathname);
|
|
1869
|
+
|
|
1870
|
+
if (decoded.startsWith(assets)) {
|
|
1871
|
+
const pathname = decoded.slice(assets.length);
|
|
1872
|
+
const file = config.kit.files.assets + pathname;
|
|
1873
|
+
|
|
1874
|
+
if (fs__default.existsSync(file) && !fs__default.statSync(file).isDirectory()) {
|
|
1875
|
+
req.url = encodeURI(pathname); // don't need query/hash
|
|
1876
|
+
asset_server(req, res);
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
if (!decoded.startsWith(config.kit.paths.base)) return not_found(res);
|
|
1882
|
+
|
|
1883
|
+
/** @type {Partial<import('types/internal').Hooks>} */
|
|
1884
|
+
const user_hooks = resolve_entry(config.kit.files.hooks)
|
|
1885
|
+
? await vite.ssrLoadModule(`/${config.kit.files.hooks}`)
|
|
1886
|
+
: {};
|
|
1887
|
+
|
|
1888
|
+
/** @type {import('types/internal').Hooks} */
|
|
1889
|
+
const hooks = {
|
|
1890
|
+
getSession: user_hooks.getSession || (() => ({})),
|
|
1891
|
+
handle: user_hooks.handle || (({ event, resolve }) => resolve(event)),
|
|
1892
|
+
handleError:
|
|
1893
|
+
user_hooks.handleError ||
|
|
1894
|
+
(({ /** @type {Error & { frame?: string }} */ error }) => {
|
|
1895
|
+
console.error($.bold().red(error.message));
|
|
1896
|
+
if (error.frame) {
|
|
1897
|
+
console.error($.gray(error.frame));
|
|
1898
|
+
}
|
|
1899
|
+
if (error.stack) {
|
|
1900
|
+
console.error($.gray(error.stack));
|
|
1901
|
+
}
|
|
1902
|
+
}),
|
|
1903
|
+
externalFetch: user_hooks.externalFetch || fetch
|
|
1904
|
+
};
|
|
1905
|
+
|
|
1906
|
+
if (/** @type {any} */ (hooks).getContext) {
|
|
1907
|
+
// TODO remove this for 1.0
|
|
1908
|
+
throw new Error(
|
|
1909
|
+
'The getContext hook has been removed. See https://kit.svelte.dev/docs#hooks'
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
if (/** @type {any} */ (hooks).serverFetch) {
|
|
1914
|
+
// TODO remove this for 1.0
|
|
1915
|
+
throw new Error('The serverFetch hook has been renamed to externalFetch.');
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
const root = (await vite.ssrLoadModule(`/${SVELTE_KIT}/generated/root.svelte`)).default;
|
|
1919
|
+
const paths = await vite.ssrLoadModule(
|
|
1920
|
+
true ? `/${SVELTE_KIT}/runtime/paths.js` : `/@fs${runtime}/paths.js`
|
|
1921
|
+
);
|
|
1922
|
+
|
|
1923
|
+
paths.set_paths({
|
|
1924
|
+
base: config.kit.paths.base,
|
|
1925
|
+
assets
|
|
1926
|
+
});
|
|
1927
|
+
|
|
1928
|
+
let request;
|
|
1929
|
+
|
|
1930
|
+
try {
|
|
1931
|
+
request = await getRequest(base, req);
|
|
1932
|
+
} catch (/** @type {any} */ err) {
|
|
1933
|
+
res.statusCode = err.status || 400;
|
|
1934
|
+
return res.end(err.reason || 'Invalid request body');
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
const rendered = await respond(request, {
|
|
1938
|
+
amp: config.kit.amp,
|
|
1939
|
+
dev: true,
|
|
1940
|
+
floc: config.kit.floc,
|
|
1941
|
+
get_stack: (error) => {
|
|
1942
|
+
vite.ssrFixStacktrace(error);
|
|
1943
|
+
return error.stack;
|
|
1944
|
+
},
|
|
1945
|
+
handle_error: (error, event) => {
|
|
1946
|
+
vite.ssrFixStacktrace(error);
|
|
1947
|
+
hooks.handleError({
|
|
1948
|
+
error,
|
|
1949
|
+
event,
|
|
1950
|
+
|
|
1951
|
+
// TODO remove for 1.0
|
|
1952
|
+
// @ts-expect-error
|
|
1953
|
+
get request() {
|
|
1954
|
+
throw new Error(
|
|
1955
|
+
'request in handleError has been replaced with event. See https://github.com/sveltejs/kit/pull/3384 for details'
|
|
1956
|
+
);
|
|
1957
|
+
}
|
|
1958
|
+
});
|
|
1959
|
+
},
|
|
1960
|
+
hooks,
|
|
1961
|
+
hydrate: config.kit.hydrate,
|
|
1962
|
+
manifest,
|
|
1963
|
+
method_override: config.kit.methodOverride,
|
|
1964
|
+
paths: {
|
|
1965
|
+
base: config.kit.paths.base,
|
|
1966
|
+
assets
|
|
1967
|
+
},
|
|
1968
|
+
prefix: '',
|
|
1969
|
+
prerender: config.kit.prerender.enabled,
|
|
1970
|
+
read: (file) => fs__default.readFileSync(path__default.join(config.kit.files.assets, file)),
|
|
1971
|
+
root,
|
|
1972
|
+
router: config.kit.router,
|
|
1973
|
+
target: config.kit.target,
|
|
1974
|
+
template: ({ head, body, assets }) => {
|
|
1975
|
+
let rendered = load_template(cwd, config)
|
|
1976
|
+
.replace(/%svelte\.assets%/g, assets)
|
|
1977
|
+
// head and body must be replaced last, in case someone tries to sneak in %svelte.assets% etc
|
|
1978
|
+
.replace('%svelte.head%', () => head)
|
|
1979
|
+
.replace('%svelte.body%', () => body);
|
|
1980
|
+
|
|
1981
|
+
if (amp) {
|
|
1982
|
+
const result = amp.validateString(rendered);
|
|
1983
|
+
|
|
1984
|
+
if (result.status !== 'PASS') {
|
|
1985
|
+
const lines = rendered.split('\n');
|
|
1986
|
+
|
|
1987
|
+
/** @param {string} str */
|
|
1988
|
+
const escape = (str) =>
|
|
1989
|
+
str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1990
|
+
|
|
1991
|
+
rendered = `<!doctype html>
|
|
1992
|
+
<head>
|
|
1993
|
+
<meta charset="utf-8" />
|
|
1994
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1995
|
+
<style>
|
|
1996
|
+
body {
|
|
1997
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
1998
|
+
color: #333;
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
pre {
|
|
2002
|
+
background: #f4f4f4;
|
|
2003
|
+
padding: 1em;
|
|
2004
|
+
overflow-x: auto;
|
|
2005
|
+
}
|
|
2006
|
+
</style>
|
|
2007
|
+
</head>
|
|
2008
|
+
<h1>AMP validation failed</h1>
|
|
2009
|
+
|
|
2010
|
+
${result.errors
|
|
2011
|
+
.map(
|
|
2012
|
+
(error) => `
|
|
2013
|
+
<h2>${error.severity}</h2>
|
|
2014
|
+
<p>Line ${error.line}, column ${error.col}: ${error.message} (<a href="${error.specUrl}">${
|
|
2015
|
+
error.code
|
|
2016
|
+
}</a>)</p>
|
|
2017
|
+
<pre>${escape(lines[error.line - 1])}</pre>
|
|
2018
|
+
`
|
|
2019
|
+
)
|
|
2020
|
+
.join('\n\n')}
|
|
2021
|
+
`;
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
return rendered;
|
|
2026
|
+
},
|
|
2027
|
+
trailing_slash: config.kit.trailingSlash
|
|
2028
|
+
});
|
|
2029
|
+
|
|
2030
|
+
if (rendered) {
|
|
2031
|
+
setResponse(res, rendered);
|
|
2032
|
+
} else {
|
|
2033
|
+
not_found(res);
|
|
2034
|
+
}
|
|
2035
|
+
} catch (e) {
|
|
2036
|
+
const error = coalesce_to_error(e);
|
|
2037
|
+
vite.ssrFixStacktrace(error);
|
|
2038
|
+
res.statusCode = 500;
|
|
2039
|
+
res.end(error.stack);
|
|
2040
|
+
}
|
|
2041
|
+
});
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
/** @param {string[]} array */
|
|
2048
|
+
function get_params(array) {
|
|
2049
|
+
// given an array of params like `['x', 'y', 'z']` for
|
|
2050
|
+
// src/routes/[x]/[y]/[z]/svelte, create a function
|
|
2051
|
+
// that turns a RegExpExecArray into ({ x, y, z })
|
|
2052
|
+
|
|
2053
|
+
/** @param {RegExpExecArray} match */
|
|
2054
|
+
const fn = (match) => {
|
|
2055
|
+
/** @type {Record<string, string>} */
|
|
2056
|
+
const params = {};
|
|
2057
|
+
array.forEach((key, i) => {
|
|
2058
|
+
if (key.startsWith('...')) {
|
|
2059
|
+
params[key.slice(3)] = match[i + 1] || '';
|
|
2060
|
+
} else {
|
|
2061
|
+
params[key] = match[i + 1];
|
|
2062
|
+
}
|
|
2063
|
+
});
|
|
2064
|
+
return params;
|
|
2065
|
+
};
|
|
2066
|
+
|
|
2067
|
+
return fn;
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
/** @param {import('http').ServerResponse} res */
|
|
2071
|
+
function not_found(res) {
|
|
2072
|
+
res.statusCode = 404;
|
|
2073
|
+
res.end('Not found');
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
/**
|
|
2077
|
+
* @param {import('connect').Server} server
|
|
2078
|
+
*/
|
|
2079
|
+
function remove_html_middlewares(server) {
|
|
2080
|
+
const html_middlewares = [
|
|
2081
|
+
'viteIndexHtmlMiddleware',
|
|
2082
|
+
'vite404Middleware',
|
|
2083
|
+
'viteSpaFallbackMiddleware'
|
|
2084
|
+
];
|
|
2085
|
+
for (let i = server.stack.length - 1; i > 0; i--) {
|
|
2086
|
+
// @ts-expect-error using internals until https://github.com/vitejs/vite/pull/4640 is merged
|
|
2087
|
+
if (html_middlewares.includes(server.stack[i].handle.name)) {
|
|
2088
|
+
server.stack.splice(i, 1);
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
/**
|
|
2094
|
+
* @param {import('vite').ModuleNode} node
|
|
2095
|
+
* @param {Set<import('vite').ModuleNode>} deps
|
|
2096
|
+
*/
|
|
2097
|
+
function find_deps(node, deps) {
|
|
2098
|
+
for (const dep of node.importedModules) {
|
|
2099
|
+
if (!deps.has(dep)) {
|
|
2100
|
+
deps.add(dep);
|
|
2101
|
+
find_deps(dep, deps);
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
/**
|
|
2107
|
+
* @typedef {{
|
|
2108
|
+
* cwd: string,
|
|
2109
|
+
* port: number,
|
|
2110
|
+
* host?: string,
|
|
2111
|
+
* https: boolean,
|
|
2112
|
+
* config: import('types/config').ValidatedConfig
|
|
2113
|
+
* }} Options
|
|
2114
|
+
* @typedef {import('types/internal').SSRComponent} SSRComponent
|
|
2115
|
+
*/
|
|
2116
|
+
|
|
2117
|
+
/** @param {Options} opts */
|
|
2118
|
+
async function dev({ cwd, port, host, https, config }) {
|
|
2119
|
+
copy_assets(`${SVELTE_KIT}/runtime`);
|
|
2120
|
+
|
|
2121
|
+
const [vite_config] = deep_merge(
|
|
2122
|
+
{
|
|
2123
|
+
server: {
|
|
2124
|
+
fs: {
|
|
2125
|
+
allow: [
|
|
2126
|
+
...new Set([
|
|
2127
|
+
config.kit.files.assets,
|
|
2128
|
+
config.kit.files.lib,
|
|
2129
|
+
config.kit.files.routes,
|
|
2130
|
+
path__default.resolve(cwd, 'src'),
|
|
2131
|
+
path__default.resolve(cwd, SVELTE_KIT),
|
|
2132
|
+
path__default.resolve(cwd, 'node_modules'),
|
|
2133
|
+
path__default.resolve(vite.searchForWorkspaceRoot(cwd), 'node_modules')
|
|
2134
|
+
])
|
|
2135
|
+
]
|
|
2136
|
+
},
|
|
2137
|
+
strictPort: true
|
|
2138
|
+
}
|
|
2139
|
+
},
|
|
2140
|
+
config.kit.vite()
|
|
2141
|
+
);
|
|
2142
|
+
|
|
2143
|
+
/** @type {[any, string[]]} */
|
|
2144
|
+
const [merged_config, conflicts] = deep_merge(vite_config, {
|
|
2145
|
+
configFile: false,
|
|
2146
|
+
root: cwd,
|
|
2147
|
+
resolve: {
|
|
2148
|
+
alias: get_aliases(config)
|
|
2149
|
+
},
|
|
2150
|
+
build: {
|
|
2151
|
+
rollupOptions: {
|
|
2152
|
+
// Vite dependency crawler needs an explicit JS entry point
|
|
2153
|
+
// eventhough server otherwise works without it
|
|
2154
|
+
input: `${runtime}/client/start.js`
|
|
2155
|
+
}
|
|
2156
|
+
},
|
|
2157
|
+
plugins: [
|
|
2158
|
+
svelte({
|
|
2159
|
+
extensions: config.extensions,
|
|
2160
|
+
emitCss: !config.kit.amp,
|
|
2161
|
+
compilerOptions: {
|
|
2162
|
+
hydratable: !!config.kit.hydrate
|
|
2163
|
+
}
|
|
2164
|
+
}),
|
|
2165
|
+
await create_plugin(config, cwd)
|
|
2166
|
+
],
|
|
2167
|
+
base: '/'
|
|
2168
|
+
});
|
|
2169
|
+
|
|
2170
|
+
print_config_conflicts(conflicts, 'kit.vite.');
|
|
2171
|
+
|
|
2172
|
+
// optional config from command-line flags
|
|
2173
|
+
// these should take precedence, but not print conflict warnings
|
|
2174
|
+
if (host) {
|
|
2175
|
+
merged_config.server.host = host;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
// if https is already enabled then do nothing. it could be an object and we
|
|
2179
|
+
// don't want to overwrite with a boolean
|
|
2180
|
+
if (https && !merged_config.server.https) {
|
|
2181
|
+
merged_config.server.https = https;
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
if (port) {
|
|
2185
|
+
merged_config.server.port = port;
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
const server = await vite.createServer(merged_config);
|
|
2189
|
+
await server.listen(port);
|
|
2190
|
+
|
|
2191
|
+
const address_info = /** @type {import('net').AddressInfo} */ (
|
|
2192
|
+
/** @type {import('http').Server} */ (server.httpServer).address()
|
|
2193
|
+
);
|
|
2194
|
+
|
|
2195
|
+
return {
|
|
2196
|
+
address_info,
|
|
2197
|
+
server_config: vite_config.server,
|
|
2198
|
+
close: () => server.close()
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
export { dev };
|