@sveltejs/kit 1.7.2 → 1.8.0
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/package.json +3 -3
- package/src/core/env.js +6 -10
- package/src/core/generate_manifest/index.js +1 -1
- package/src/core/sync/write_ambient.js +6 -5
- package/src/core/sync/write_client_manifest.js +3 -2
- package/src/core/sync/write_server.js +6 -4
- package/src/exports/vite/build/utils.js +1 -0
- package/src/exports/vite/dev/index.js +15 -9
- package/src/exports/vite/index.js +67 -35
- package/src/internal.d.ts +7 -0
- package/src/runtime/app/environment.js +1 -1
- package/src/runtime/client/client.js +118 -62
- package/src/runtime/client/parse.js +2 -5
- package/src/runtime/client/start.js +5 -16
- package/src/runtime/client/types.d.ts +37 -1
- package/src/runtime/client/utils.js +1 -1
- package/src/runtime/env/dynamic/private.js +1 -1
- package/src/runtime/env/dynamic/public.js +1 -1
- package/src/runtime/server/data/index.js +118 -18
- package/src/runtime/server/index.js +1 -1
- package/src/runtime/server/page/index.js +16 -11
- package/src/runtime/server/page/load_data.js +56 -6
- package/src/runtime/server/page/render.js +253 -153
- package/src/runtime/server/page/types.d.ts +2 -2
- package/src/runtime/server/utils.js +12 -21
- package/src/runtime/{shared.js → shared-server.js} +0 -13
- package/src/utils/streaming.js +44 -0
- package/types/index.d.ts +5 -7
- package/types/internal.d.ts +49 -17
- package/src/runtime/client/ambient.d.ts +0 -30
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { disable_search, make_trackable } from '../../../utils/url.js';
|
|
2
2
|
import { unwrap_promises } from '../../../utils/promises.js';
|
|
3
|
+
import { DEV } from 'esm-env';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Calls the user's server `load` function.
|
|
@@ -14,6 +15,8 @@ import { unwrap_promises } from '../../../utils/promises.js';
|
|
|
14
15
|
export async function load_server_data({ event, state, node, parent }) {
|
|
15
16
|
if (!node?.server) return null;
|
|
16
17
|
|
|
18
|
+
let done = false;
|
|
19
|
+
|
|
17
20
|
const uses = {
|
|
18
21
|
dependencies: new Set(),
|
|
19
22
|
params: new Set(),
|
|
@@ -23,6 +26,12 @@ export async function load_server_data({ event, state, node, parent }) {
|
|
|
23
26
|
};
|
|
24
27
|
|
|
25
28
|
const url = make_trackable(event.url, () => {
|
|
29
|
+
if (DEV && done && !uses.url) {
|
|
30
|
+
console.warn(
|
|
31
|
+
`${node.server_id}: Accessing URL properties in a promise handler after \`load(...)\` has returned will not cause the function to re-run when the URL changes`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
26
35
|
uses.url = true;
|
|
27
36
|
});
|
|
28
37
|
|
|
@@ -34,6 +43,13 @@ export async function load_server_data({ event, state, node, parent }) {
|
|
|
34
43
|
...event,
|
|
35
44
|
fetch: (info, init) => {
|
|
36
45
|
const url = new URL(info instanceof Request ? info.url : info, event.url);
|
|
46
|
+
|
|
47
|
+
if (DEV && done && !uses.dependencies.has(url.href)) {
|
|
48
|
+
console.warn(
|
|
49
|
+
`${node.server_id}: Calling \`event.fetch(...)\` in a promise handler after \`load(...)\` has returned will not cause the function to re-run when the dependency is invalidated`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
37
53
|
uses.dependencies.add(url.href);
|
|
38
54
|
|
|
39
55
|
return event.fetch(info, init);
|
|
@@ -42,25 +58,54 @@ export async function load_server_data({ event, state, node, parent }) {
|
|
|
42
58
|
depends: (...deps) => {
|
|
43
59
|
for (const dep of deps) {
|
|
44
60
|
const { href } = new URL(dep, event.url);
|
|
61
|
+
|
|
62
|
+
if (DEV && done && !uses.dependencies.has(href)) {
|
|
63
|
+
console.warn(
|
|
64
|
+
`${node.server_id}: Calling \`depends(...)\` in a promise handler after \`load(...)\` has returned will not cause the function to re-run when the dependency is invalidated`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
45
68
|
uses.dependencies.add(href);
|
|
46
69
|
}
|
|
47
70
|
},
|
|
48
71
|
params: new Proxy(event.params, {
|
|
49
72
|
get: (target, key) => {
|
|
73
|
+
if (DEV && done && typeof key === 'string' && !uses.params.has(key)) {
|
|
74
|
+
console.warn(
|
|
75
|
+
`${node.server_id}: Accessing \`params.${String(
|
|
76
|
+
key
|
|
77
|
+
)}\` in a promise handler after \`load(...)\` has returned will not cause the function to re-run when the param changes`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
50
81
|
uses.params.add(key);
|
|
51
82
|
return target[/** @type {string} */ (key)];
|
|
52
83
|
}
|
|
53
84
|
}),
|
|
54
85
|
parent: async () => {
|
|
86
|
+
if (DEV && done && !uses.parent) {
|
|
87
|
+
console.warn(
|
|
88
|
+
`${node.server_id}: Calling \`parent(...)\` in a promise handler after \`load(...)\` has returned will not cause the function to re-run when parent data changes`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
55
92
|
uses.parent = true;
|
|
56
93
|
return parent();
|
|
57
94
|
},
|
|
58
|
-
route: {
|
|
59
|
-
get
|
|
95
|
+
route: new Proxy(event.route, {
|
|
96
|
+
get: (target, key) => {
|
|
97
|
+
if (DEV && done && typeof key === 'string' && !uses.route) {
|
|
98
|
+
console.warn(
|
|
99
|
+
`${node.server_id}: Accessing \`route.${String(
|
|
100
|
+
key
|
|
101
|
+
)}\` in a promise handler after \`load(...)\` has returned will not cause the function to re-run when the route changes`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
60
105
|
uses.route = true;
|
|
61
|
-
return
|
|
106
|
+
return target[/** @type {'id'} */ (key)];
|
|
62
107
|
}
|
|
63
|
-
},
|
|
108
|
+
}),
|
|
64
109
|
url
|
|
65
110
|
});
|
|
66
111
|
|
|
@@ -69,6 +114,8 @@ export async function load_server_data({ event, state, node, parent }) {
|
|
|
69
114
|
validate_load_response(data, /** @type {string} */ (event.route.id));
|
|
70
115
|
}
|
|
71
116
|
|
|
117
|
+
done = true;
|
|
118
|
+
|
|
72
119
|
return {
|
|
73
120
|
type: 'data',
|
|
74
121
|
data,
|
|
@@ -89,7 +136,7 @@ export async function load_server_data({ event, state, node, parent }) {
|
|
|
89
136
|
* state: import('types').SSRState;
|
|
90
137
|
* csr: boolean;
|
|
91
138
|
* }} opts
|
|
92
|
-
* @returns {Promise<Record<string, any
|
|
139
|
+
* @returns {Promise<Record<string, any | Promise<any>> | null>}
|
|
93
140
|
*/
|
|
94
141
|
export async function load_data({
|
|
95
142
|
event,
|
|
@@ -119,7 +166,10 @@ export async function load_data({
|
|
|
119
166
|
});
|
|
120
167
|
|
|
121
168
|
const data = result ? await unwrap_promises(result) : null;
|
|
122
|
-
|
|
169
|
+
if (__SVELTEKIT_DEV__) {
|
|
170
|
+
validate_load_response(data, /** @type {string} */ (event.route.id));
|
|
171
|
+
}
|
|
172
|
+
|
|
123
173
|
return data;
|
|
124
174
|
}
|
|
125
175
|
|
|
@@ -7,9 +7,10 @@ import { serialize_data } from './serialize_data.js';
|
|
|
7
7
|
import { s } from '../../../utils/misc.js';
|
|
8
8
|
import { Csp } from './csp.js';
|
|
9
9
|
import { uneval_action_response } from './actions.js';
|
|
10
|
-
import { clarify_devalue_error } from '../utils.js';
|
|
11
|
-
import {
|
|
10
|
+
import { clarify_devalue_error, stringify_uses, handle_error_and_jsonify } from '../utils.js';
|
|
11
|
+
import { public_env } from '../../shared-server.js';
|
|
12
12
|
import { text } from '../../../exports/index.js';
|
|
13
|
+
import { create_async_iterator } from '../../../utils/streaming.js';
|
|
13
14
|
|
|
14
15
|
// TODO rename this function/module
|
|
15
16
|
|
|
@@ -57,11 +58,11 @@ export async function render_response({
|
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
const {
|
|
61
|
+
const { client } = manifest._;
|
|
61
62
|
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
const fonts = new Set(
|
|
63
|
+
const modulepreloads = new Set([...client.start.imports, ...client.app.imports]);
|
|
64
|
+
const stylesheets = new Set(client.app.stylesheets);
|
|
65
|
+
const fonts = new Set(client.app.fonts);
|
|
65
66
|
|
|
66
67
|
/** @type {Set<string>} */
|
|
67
68
|
const link_header_preloads = new Set();
|
|
@@ -141,17 +142,9 @@ export async function render_response({
|
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
for (const { node } of branch) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (node.stylesheets) {
|
|
149
|
-
node.stylesheets.forEach((url) => stylesheets.add(url));
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (node.fonts) {
|
|
153
|
-
node.fonts.forEach((url) => fonts.add(url));
|
|
154
|
-
}
|
|
145
|
+
for (const url of node.imports) modulepreloads.add(url);
|
|
146
|
+
for (const url of node.stylesheets) stylesheets.add(url);
|
|
147
|
+
for (const url of node.fonts) fonts.add(url);
|
|
155
148
|
|
|
156
149
|
if (node.inline_styles) {
|
|
157
150
|
Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v));
|
|
@@ -161,34 +154,42 @@ export async function render_response({
|
|
|
161
154
|
rendered = { head: '', html: '', css: { code: '', map: null } };
|
|
162
155
|
}
|
|
163
156
|
|
|
164
|
-
let head = '';
|
|
165
|
-
let body = rendered.html;
|
|
166
|
-
|
|
167
|
-
const csp = new Csp(options.csp, {
|
|
168
|
-
prerender: !!state.prerendering
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
const target = hash(body);
|
|
172
|
-
|
|
173
157
|
/**
|
|
174
158
|
* The prefix to use for static assets. Replaces `%sveltekit.assets%` in the template
|
|
175
159
|
* @type {string}
|
|
176
160
|
*/
|
|
177
161
|
let resolved_assets;
|
|
178
162
|
|
|
163
|
+
/**
|
|
164
|
+
* An expression that will evaluate in the client to determine the resolved asset path
|
|
165
|
+
*/
|
|
166
|
+
let asset_expression;
|
|
167
|
+
|
|
179
168
|
if (assets) {
|
|
180
169
|
// if an asset path is specified, use it
|
|
181
170
|
resolved_assets = assets;
|
|
171
|
+
asset_expression = s(assets);
|
|
182
172
|
} else if (state.prerendering?.fallback) {
|
|
183
173
|
// if we're creating a fallback page, asset paths need to be root-relative
|
|
184
174
|
resolved_assets = base;
|
|
175
|
+
asset_expression = s(base);
|
|
185
176
|
} else {
|
|
186
177
|
// otherwise we want asset paths to be relative to the page, so that they
|
|
187
178
|
// will work in odd contexts like IPFS, the internet archive, and so on
|
|
188
179
|
const segments = event.url.pathname.slice(base.length).split('/').slice(2);
|
|
189
180
|
resolved_assets = segments.length > 0 ? segments.map(() => '..').join('/') : '.';
|
|
181
|
+
asset_expression = `new URL(${s(
|
|
182
|
+
resolved_assets
|
|
183
|
+
)}, location.href).pathname.replace(/^\\\/$/, '')`;
|
|
190
184
|
}
|
|
191
185
|
|
|
186
|
+
let head = '';
|
|
187
|
+
let body = rendered.html;
|
|
188
|
+
|
|
189
|
+
const csp = new Csp(options.csp, {
|
|
190
|
+
prerender: !!state.prerendering
|
|
191
|
+
});
|
|
192
|
+
|
|
192
193
|
/** @param {string} path */
|
|
193
194
|
const prefixed = (path) => {
|
|
194
195
|
if (path.startsWith('/')) {
|
|
@@ -200,48 +201,6 @@ export async function render_response({
|
|
|
200
201
|
return `${resolved_assets}/${path}`;
|
|
201
202
|
};
|
|
202
203
|
|
|
203
|
-
const serialized = { data: '', form: 'null', error: 'null' };
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
serialized.data = `[${branch
|
|
207
|
-
.map(({ server_data }) => {
|
|
208
|
-
if (server_data?.type === 'data') {
|
|
209
|
-
const data = devalue.uneval(server_data.data);
|
|
210
|
-
|
|
211
|
-
const uses = [];
|
|
212
|
-
if (server_data.uses.dependencies.size > 0) {
|
|
213
|
-
uses.push(`dependencies:${s(Array.from(server_data.uses.dependencies))}`);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (server_data.uses.params.size > 0) {
|
|
217
|
-
uses.push(`params:${s(Array.from(server_data.uses.params))}`);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (server_data.uses.parent) uses.push(`parent:1`);
|
|
221
|
-
if (server_data.uses.route) uses.push(`route:1`);
|
|
222
|
-
if (server_data.uses.url) uses.push(`url:1`);
|
|
223
|
-
|
|
224
|
-
return `{type:"data",data:${data},uses:{${uses.join(',')}}${
|
|
225
|
-
server_data.slash ? `,slash:${s(server_data.slash)}` : ''
|
|
226
|
-
}}`;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return s(server_data);
|
|
230
|
-
})
|
|
231
|
-
.join(',')}]`;
|
|
232
|
-
} catch (e) {
|
|
233
|
-
const error = /** @type {any} */ (e);
|
|
234
|
-
throw new Error(clarify_devalue_error(event, error));
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (form_value) {
|
|
238
|
-
serialized.form = uneval_action_response(form_value, /** @type {string} */ (event.route.id));
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (error) {
|
|
242
|
-
serialized.error = devalue.uneval(error);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
204
|
if (inline_styles.size > 0) {
|
|
246
205
|
const content = Array.from(inline_styles.values()).join('\n');
|
|
247
206
|
|
|
@@ -289,18 +248,86 @@ export async function render_response({
|
|
|
289
248
|
}
|
|
290
249
|
}
|
|
291
250
|
|
|
251
|
+
const global = `__sveltekit_${options.version_hash}`;
|
|
252
|
+
|
|
253
|
+
const { data, chunks } = get_data(
|
|
254
|
+
event,
|
|
255
|
+
options,
|
|
256
|
+
branch.map((b) => b.server_data),
|
|
257
|
+
global
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
if (page_config.ssr && page_config.csr) {
|
|
261
|
+
body += `\n\t\t\t${fetched
|
|
262
|
+
.map((item) =>
|
|
263
|
+
serialize_data(item, resolve_opts.filterSerializedResponseHeaders, !!state.prerendering)
|
|
264
|
+
)
|
|
265
|
+
.join('\n\t\t\t')}`;
|
|
266
|
+
}
|
|
267
|
+
|
|
292
268
|
if (page_config.csr) {
|
|
293
|
-
const
|
|
294
|
-
|
|
269
|
+
const included_modulepreloads = Array.from(modulepreloads, (dep) => prefixed(dep)).filter(
|
|
270
|
+
(path) => resolve_opts.preload({ type: 'js', path })
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
for (const path of included_modulepreloads) {
|
|
274
|
+
// we use modulepreload with the Link header for Chrome, along with
|
|
275
|
+
// <link rel="preload"> for Safari. This results in the fastest loading in
|
|
276
|
+
// the most used browsers, with no double-loading. Note that we need to use
|
|
277
|
+
// .mjs extensions for `preload` to behave like `modulepreload` in Chrome
|
|
278
|
+
link_header_preloads.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
|
|
279
|
+
head += `\n\t\t<link rel="preload" as="script" crossorigin="anonymous" href="${path}">`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const blocks = [];
|
|
283
|
+
|
|
284
|
+
const properties = [
|
|
295
285
|
`env: ${s(public_env)}`,
|
|
296
|
-
`
|
|
297
|
-
`
|
|
286
|
+
`assets: ${asset_expression}`,
|
|
287
|
+
`element: document.currentScript.parentElement`
|
|
298
288
|
];
|
|
299
289
|
|
|
290
|
+
if (chunks) {
|
|
291
|
+
blocks.push(`const deferred = new Map();`);
|
|
292
|
+
|
|
293
|
+
properties.push(`defer: (id) => new Promise((fulfil, reject) => {
|
|
294
|
+
deferred.set(id, { fulfil, reject });
|
|
295
|
+
})`);
|
|
296
|
+
|
|
297
|
+
properties.push(`resolve: ({ id, data, error }) => {
|
|
298
|
+
const { fulfil, reject } = deferred.get(id);
|
|
299
|
+
deferred.delete(id);
|
|
300
|
+
|
|
301
|
+
if (error) reject(error);
|
|
302
|
+
else fulfil(data);
|
|
303
|
+
}`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
blocks.push(`${global} = {
|
|
307
|
+
${properties.join(',\n\t\t\t\t\t\t')}
|
|
308
|
+
};`);
|
|
309
|
+
|
|
310
|
+
const args = [`app`, `${global}.element`];
|
|
311
|
+
|
|
300
312
|
if (page_config.ssr) {
|
|
313
|
+
const serialized = { form: 'null', error: 'null' };
|
|
314
|
+
|
|
315
|
+
blocks.push(`const data = ${data};`);
|
|
316
|
+
|
|
317
|
+
if (form_value) {
|
|
318
|
+
serialized.form = uneval_action_response(
|
|
319
|
+
form_value,
|
|
320
|
+
/** @type {string} */ (event.route.id)
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (error) {
|
|
325
|
+
serialized.error = devalue.uneval(error);
|
|
326
|
+
}
|
|
327
|
+
|
|
301
328
|
const hydrate = [
|
|
302
329
|
`node_ids: [${branch.map(({ node }) => node.index).join(', ')}]`,
|
|
303
|
-
`data
|
|
330
|
+
`data`,
|
|
304
331
|
`form: ${serialized.form}`,
|
|
305
332
|
`error: ${serialized.error}`
|
|
306
333
|
];
|
|
@@ -313,67 +340,44 @@ export async function render_response({
|
|
|
313
340
|
hydrate.push(`params: ${devalue.uneval(event.params)}`, `route: ${s(event.route)}`);
|
|
314
341
|
}
|
|
315
342
|
|
|
316
|
-
|
|
343
|
+
args.push(`{\n\t\t\t\t\t\t\t${hydrate.join(',\n\t\t\t\t\t\t\t')}\n\t\t\t\t\t\t}`);
|
|
317
344
|
}
|
|
318
345
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if (
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
346
|
+
blocks.push(`Promise.all([
|
|
347
|
+
import(${s(prefixed(client.start.file))}),
|
|
348
|
+
import(${s(prefixed(client.app.file))})
|
|
349
|
+
]).then(([kit, app]) => {
|
|
350
|
+
kit.start(${args.join(', ')});
|
|
351
|
+
});`);
|
|
352
|
+
|
|
353
|
+
if (options.service_worker) {
|
|
354
|
+
const opts = __SVELTEKIT_DEV__ ? `, { type: 'module' }` : '';
|
|
355
|
+
|
|
356
|
+
// we use an anonymous function instead of an arrow function to support
|
|
357
|
+
// older browsers (https://github.com/sveltejs/kit/pull/5417)
|
|
358
|
+
blocks.push(`if ('serviceWorker' in navigator) {
|
|
359
|
+
addEventListener('load', function () {
|
|
360
|
+
navigator.serviceWorker.register('${prefixed('service-worker.js')}'${opts});
|
|
361
|
+
});
|
|
362
|
+
}`);
|
|
337
363
|
}
|
|
338
364
|
|
|
339
|
-
const
|
|
340
|
-
|
|
365
|
+
const init_app = `
|
|
366
|
+
{
|
|
367
|
+
${blocks.join('\n\n\t\t\t\t\t')}
|
|
368
|
+
}
|
|
369
|
+
`;
|
|
341
370
|
csp.add_script(init_app);
|
|
342
371
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
body += `\n\t\t<script ${attributes.join(' ')}>${init_app}</script>`;
|
|
372
|
+
body += `\n\t\t\t<script${
|
|
373
|
+
csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''
|
|
374
|
+
}>${init_app}</script>\n\t\t`;
|
|
348
375
|
}
|
|
349
376
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
)
|
|
355
|
-
.join('\n\t')}`;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if (options.service_worker) {
|
|
359
|
-
const opts = __SVELTEKIT_DEV__ ? `, { type: 'module' }` : '';
|
|
360
|
-
|
|
361
|
-
// we use an anonymous function instead of an arrow function to support
|
|
362
|
-
// older browsers (https://github.com/sveltejs/kit/pull/5417)
|
|
363
|
-
const init_service_worker = `
|
|
364
|
-
if ('serviceWorker' in navigator) {
|
|
365
|
-
addEventListener('load', function () {
|
|
366
|
-
navigator.serviceWorker.register('${prefixed('service-worker.js')}'${opts});
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
`;
|
|
370
|
-
|
|
371
|
-
// always include service worker unless it's turned off explicitly
|
|
372
|
-
csp.add_script(init_service_worker);
|
|
373
|
-
|
|
374
|
-
head += `
|
|
375
|
-
<script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`;
|
|
376
|
-
}
|
|
377
|
+
const headers = new Headers({
|
|
378
|
+
'x-sveltekit-page': 'true',
|
|
379
|
+
'content-type': 'text/html'
|
|
380
|
+
});
|
|
377
381
|
|
|
378
382
|
if (state.prerendering) {
|
|
379
383
|
// TODO read headers set with setHeaders and convert into http-equiv where possible
|
|
@@ -391,6 +395,19 @@ export async function render_response({
|
|
|
391
395
|
if (http_equiv.length > 0) {
|
|
392
396
|
head = http_equiv.join('\n') + head;
|
|
393
397
|
}
|
|
398
|
+
} else {
|
|
399
|
+
const csp_header = csp.csp_provider.get_header();
|
|
400
|
+
if (csp_header) {
|
|
401
|
+
headers.set('content-security-policy', csp_header);
|
|
402
|
+
}
|
|
403
|
+
const report_only_header = csp.report_only_provider.get_header();
|
|
404
|
+
if (report_only_header) {
|
|
405
|
+
headers.set('content-security-policy-report-only', report_only_header);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (link_header_preloads.size) {
|
|
409
|
+
headers.set('link', Array.from(link_header_preloads).join(', '));
|
|
410
|
+
}
|
|
394
411
|
}
|
|
395
412
|
|
|
396
413
|
// add the content after the script/css links so the link elements are parsed first
|
|
@@ -411,39 +428,122 @@ export async function render_response({
|
|
|
411
428
|
done: true
|
|
412
429
|
})) || '';
|
|
413
430
|
|
|
414
|
-
if (
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
)
|
|
431
|
+
if (!chunks) {
|
|
432
|
+
headers.set('etag', `"${hash(transformed)}"`);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (DEV) {
|
|
436
|
+
if (page_config.csr) {
|
|
437
|
+
if (transformed.split('<!--').length < html.split('<!--').length) {
|
|
438
|
+
// the \u001B stuff is ANSI codes, so that we don't need to add a library to the runtime
|
|
439
|
+
// https://svelte.dev/repl/1b3f49696f0c44c881c34587f2537aa2
|
|
440
|
+
console.warn(
|
|
441
|
+
"\u001B[1m\u001B[31mRemoving comments in transformPageChunk can break Svelte's hydration\u001B[39m\u001B[22m"
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
} else {
|
|
445
|
+
if (chunks) {
|
|
446
|
+
console.warn(
|
|
447
|
+
'\u001B[1m\u001B[31mReturning promises from server `load` functions will only work if `csr === true`\u001B[39m\u001B[22m'
|
|
448
|
+
);
|
|
449
|
+
}
|
|
421
450
|
}
|
|
422
451
|
}
|
|
423
452
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
453
|
+
return !chunks
|
|
454
|
+
? text(transformed, {
|
|
455
|
+
status,
|
|
456
|
+
headers
|
|
457
|
+
})
|
|
458
|
+
: new Response(
|
|
459
|
+
new ReadableStream({
|
|
460
|
+
async start(controller) {
|
|
461
|
+
controller.enqueue(transformed);
|
|
462
|
+
for await (const chunk of chunks) {
|
|
463
|
+
controller.enqueue(chunk);
|
|
464
|
+
}
|
|
465
|
+
controller.close();
|
|
466
|
+
}
|
|
467
|
+
}),
|
|
468
|
+
{
|
|
469
|
+
headers: {
|
|
470
|
+
'content-type': 'text/html'
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
);
|
|
474
|
+
}
|
|
429
475
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
476
|
+
/**
|
|
477
|
+
* If the serialized data contains promises, `chunks` will be an
|
|
478
|
+
* async iterable containing their resolutions
|
|
479
|
+
* @param {import('types').RequestEvent} event
|
|
480
|
+
* @param {import('types').SSROptions} options
|
|
481
|
+
* @param {Array<import('types').ServerDataNode | null>} nodes
|
|
482
|
+
* @param {string} global
|
|
483
|
+
* @returns {{ data: string, chunks: AsyncIterable<string> | null }}
|
|
484
|
+
*/
|
|
485
|
+
function get_data(event, options, nodes, global) {
|
|
486
|
+
let promise_id = 1;
|
|
487
|
+
let count = 0;
|
|
488
|
+
|
|
489
|
+
const { iterator, push, done } = create_async_iterator();
|
|
490
|
+
|
|
491
|
+
/** @param {any} thing */
|
|
492
|
+
function replacer(thing) {
|
|
493
|
+
if (typeof thing?.then === 'function') {
|
|
494
|
+
const id = promise_id++;
|
|
495
|
+
count += 1;
|
|
496
|
+
|
|
497
|
+
thing
|
|
498
|
+
.then(/** @param {any} data */ (data) => ({ data }))
|
|
499
|
+
.catch(
|
|
500
|
+
/** @param {any} error */ async (error) => ({
|
|
501
|
+
error: await handle_error_and_jsonify(event, options, error)
|
|
502
|
+
})
|
|
503
|
+
)
|
|
504
|
+
.then(
|
|
505
|
+
/**
|
|
506
|
+
* @param {{data: any; error: any}} result
|
|
507
|
+
*/
|
|
508
|
+
async ({ data, error }) => {
|
|
509
|
+
count -= 1;
|
|
510
|
+
|
|
511
|
+
let str;
|
|
512
|
+
try {
|
|
513
|
+
str = devalue.uneval({ id, data, error }, replacer);
|
|
514
|
+
} catch (e) {
|
|
515
|
+
error = await handle_error_and_jsonify(
|
|
516
|
+
event,
|
|
517
|
+
options,
|
|
518
|
+
new Error(`Failed to serialize promise while rendering ${event.route.id}`)
|
|
519
|
+
);
|
|
520
|
+
data = undefined;
|
|
521
|
+
str = devalue.uneval({ id, data, error }, replacer);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
push(`\n<script>${global}.resolve(${str})</script>`);
|
|
525
|
+
if (count === 0) done();
|
|
526
|
+
}
|
|
527
|
+
);
|
|
439
528
|
|
|
440
|
-
|
|
441
|
-
headers.set('link', Array.from(link_header_preloads).join(', '));
|
|
529
|
+
return `${global}.defer(${id})`;
|
|
442
530
|
}
|
|
443
531
|
}
|
|
444
532
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
533
|
+
try {
|
|
534
|
+
const strings = nodes.map((node) => {
|
|
535
|
+
if (!node) return 'null';
|
|
536
|
+
|
|
537
|
+
return `{"type":"data","data":${devalue.uneval(node.data, replacer)},${stringify_uses(node)}${
|
|
538
|
+
node.slash ? `,"slash":${JSON.stringify(node.slash)}` : ''
|
|
539
|
+
}}`;
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
return {
|
|
543
|
+
data: `[${strings.join(',')}]`,
|
|
544
|
+
chunks: count > 0 ? iterator : null
|
|
545
|
+
};
|
|
546
|
+
} catch (e) {
|
|
547
|
+
throw new Error(clarify_devalue_error(event, /** @type {any} */ (e)));
|
|
548
|
+
}
|
|
449
549
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CookieSerializeOptions } from 'cookie';
|
|
2
|
-
import { SSRNode, CspDirectives } from 'types';
|
|
2
|
+
import { SSRNode, CspDirectives, ServerDataNode } from 'types';
|
|
3
3
|
|
|
4
4
|
export interface Fetched {
|
|
5
5
|
url: string;
|
|
@@ -13,7 +13,7 @@ export interface Fetched {
|
|
|
13
13
|
export type Loaded = {
|
|
14
14
|
node: SSRNode;
|
|
15
15
|
data: Record<string, any> | null;
|
|
16
|
-
server_data:
|
|
16
|
+
server_data: ServerDataNode | null;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
type CspMode = 'hash' | 'nonce' | 'auto';
|