@sveltejs/kit 1.7.2 → 1.8.1
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 +122 -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 +257 -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
|
|
|
@@ -18,6 +19,8 @@ const updated = {
|
|
|
18
19
|
check: () => false
|
|
19
20
|
};
|
|
20
21
|
|
|
22
|
+
const encoder = new TextEncoder();
|
|
23
|
+
|
|
21
24
|
/**
|
|
22
25
|
* Creates the HTML response.
|
|
23
26
|
* @param {{
|
|
@@ -57,11 +60,11 @@ export async function render_response({
|
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
const {
|
|
63
|
+
const { client } = manifest._;
|
|
61
64
|
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
const fonts = new Set(
|
|
65
|
+
const modulepreloads = new Set([...client.start.imports, ...client.app.imports]);
|
|
66
|
+
const stylesheets = new Set(client.app.stylesheets);
|
|
67
|
+
const fonts = new Set(client.app.fonts);
|
|
65
68
|
|
|
66
69
|
/** @type {Set<string>} */
|
|
67
70
|
const link_header_preloads = new Set();
|
|
@@ -141,17 +144,9 @@ export async function render_response({
|
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
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
|
-
}
|
|
147
|
+
for (const url of node.imports) modulepreloads.add(url);
|
|
148
|
+
for (const url of node.stylesheets) stylesheets.add(url);
|
|
149
|
+
for (const url of node.fonts) fonts.add(url);
|
|
155
150
|
|
|
156
151
|
if (node.inline_styles) {
|
|
157
152
|
Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v));
|
|
@@ -161,34 +156,42 @@ export async function render_response({
|
|
|
161
156
|
rendered = { head: '', html: '', css: { code: '', map: null } };
|
|
162
157
|
}
|
|
163
158
|
|
|
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
159
|
/**
|
|
174
160
|
* The prefix to use for static assets. Replaces `%sveltekit.assets%` in the template
|
|
175
161
|
* @type {string}
|
|
176
162
|
*/
|
|
177
163
|
let resolved_assets;
|
|
178
164
|
|
|
165
|
+
/**
|
|
166
|
+
* An expression that will evaluate in the client to determine the resolved asset path
|
|
167
|
+
*/
|
|
168
|
+
let asset_expression;
|
|
169
|
+
|
|
179
170
|
if (assets) {
|
|
180
171
|
// if an asset path is specified, use it
|
|
181
172
|
resolved_assets = assets;
|
|
173
|
+
asset_expression = s(assets);
|
|
182
174
|
} else if (state.prerendering?.fallback) {
|
|
183
175
|
// if we're creating a fallback page, asset paths need to be root-relative
|
|
184
176
|
resolved_assets = base;
|
|
177
|
+
asset_expression = s(base);
|
|
185
178
|
} else {
|
|
186
179
|
// otherwise we want asset paths to be relative to the page, so that they
|
|
187
180
|
// will work in odd contexts like IPFS, the internet archive, and so on
|
|
188
181
|
const segments = event.url.pathname.slice(base.length).split('/').slice(2);
|
|
189
182
|
resolved_assets = segments.length > 0 ? segments.map(() => '..').join('/') : '.';
|
|
183
|
+
asset_expression = `new URL(${s(
|
|
184
|
+
resolved_assets
|
|
185
|
+
)}, location.href).pathname.replace(/^\\\/$/, '')`;
|
|
190
186
|
}
|
|
191
187
|
|
|
188
|
+
let head = '';
|
|
189
|
+
let body = rendered.html;
|
|
190
|
+
|
|
191
|
+
const csp = new Csp(options.csp, {
|
|
192
|
+
prerender: !!state.prerendering
|
|
193
|
+
});
|
|
194
|
+
|
|
192
195
|
/** @param {string} path */
|
|
193
196
|
const prefixed = (path) => {
|
|
194
197
|
if (path.startsWith('/')) {
|
|
@@ -200,48 +203,6 @@ export async function render_response({
|
|
|
200
203
|
return `${resolved_assets}/${path}`;
|
|
201
204
|
};
|
|
202
205
|
|
|
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
206
|
if (inline_styles.size > 0) {
|
|
246
207
|
const content = Array.from(inline_styles.values()).join('\n');
|
|
247
208
|
|
|
@@ -289,18 +250,86 @@ export async function render_response({
|
|
|
289
250
|
}
|
|
290
251
|
}
|
|
291
252
|
|
|
253
|
+
const global = `__sveltekit_${options.version_hash}`;
|
|
254
|
+
|
|
255
|
+
const { data, chunks } = get_data(
|
|
256
|
+
event,
|
|
257
|
+
options,
|
|
258
|
+
branch.map((b) => b.server_data),
|
|
259
|
+
global
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
if (page_config.ssr && page_config.csr) {
|
|
263
|
+
body += `\n\t\t\t${fetched
|
|
264
|
+
.map((item) =>
|
|
265
|
+
serialize_data(item, resolve_opts.filterSerializedResponseHeaders, !!state.prerendering)
|
|
266
|
+
)
|
|
267
|
+
.join('\n\t\t\t')}`;
|
|
268
|
+
}
|
|
269
|
+
|
|
292
270
|
if (page_config.csr) {
|
|
293
|
-
const
|
|
294
|
-
|
|
271
|
+
const included_modulepreloads = Array.from(modulepreloads, (dep) => prefixed(dep)).filter(
|
|
272
|
+
(path) => resolve_opts.preload({ type: 'js', path })
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
for (const path of included_modulepreloads) {
|
|
276
|
+
// we use modulepreload with the Link header for Chrome, along with
|
|
277
|
+
// <link rel="preload"> for Safari. This results in the fastest loading in
|
|
278
|
+
// the most used browsers, with no double-loading. Note that we need to use
|
|
279
|
+
// .mjs extensions for `preload` to behave like `modulepreload` in Chrome
|
|
280
|
+
link_header_preloads.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
|
|
281
|
+
head += `\n\t\t<link rel="preload" as="script" crossorigin="anonymous" href="${path}">`;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const blocks = [];
|
|
285
|
+
|
|
286
|
+
const properties = [
|
|
295
287
|
`env: ${s(public_env)}`,
|
|
296
|
-
`
|
|
297
|
-
`
|
|
288
|
+
`assets: ${asset_expression}`,
|
|
289
|
+
`element: document.currentScript.parentElement`
|
|
298
290
|
];
|
|
299
291
|
|
|
292
|
+
if (chunks) {
|
|
293
|
+
blocks.push(`const deferred = new Map();`);
|
|
294
|
+
|
|
295
|
+
properties.push(`defer: (id) => new Promise((fulfil, reject) => {
|
|
296
|
+
deferred.set(id, { fulfil, reject });
|
|
297
|
+
})`);
|
|
298
|
+
|
|
299
|
+
properties.push(`resolve: ({ id, data, error }) => {
|
|
300
|
+
const { fulfil, reject } = deferred.get(id);
|
|
301
|
+
deferred.delete(id);
|
|
302
|
+
|
|
303
|
+
if (error) reject(error);
|
|
304
|
+
else fulfil(data);
|
|
305
|
+
}`);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
blocks.push(`${global} = {
|
|
309
|
+
${properties.join(',\n\t\t\t\t\t\t')}
|
|
310
|
+
};`);
|
|
311
|
+
|
|
312
|
+
const args = [`app`, `${global}.element`];
|
|
313
|
+
|
|
300
314
|
if (page_config.ssr) {
|
|
315
|
+
const serialized = { form: 'null', error: 'null' };
|
|
316
|
+
|
|
317
|
+
blocks.push(`const data = ${data};`);
|
|
318
|
+
|
|
319
|
+
if (form_value) {
|
|
320
|
+
serialized.form = uneval_action_response(
|
|
321
|
+
form_value,
|
|
322
|
+
/** @type {string} */ (event.route.id)
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (error) {
|
|
327
|
+
serialized.error = devalue.uneval(error);
|
|
328
|
+
}
|
|
329
|
+
|
|
301
330
|
const hydrate = [
|
|
302
331
|
`node_ids: [${branch.map(({ node }) => node.index).join(', ')}]`,
|
|
303
|
-
`data
|
|
332
|
+
`data`,
|
|
304
333
|
`form: ${serialized.form}`,
|
|
305
334
|
`error: ${serialized.error}`
|
|
306
335
|
];
|
|
@@ -313,67 +342,44 @@ export async function render_response({
|
|
|
313
342
|
hydrate.push(`params: ${devalue.uneval(event.params)}`, `route: ${s(event.route)}`);
|
|
314
343
|
}
|
|
315
344
|
|
|
316
|
-
|
|
345
|
+
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
346
|
}
|
|
318
347
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if (
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
348
|
+
blocks.push(`Promise.all([
|
|
349
|
+
import(${s(prefixed(client.start.file))}),
|
|
350
|
+
import(${s(prefixed(client.app.file))})
|
|
351
|
+
]).then(([kit, app]) => {
|
|
352
|
+
kit.start(${args.join(', ')});
|
|
353
|
+
});`);
|
|
354
|
+
|
|
355
|
+
if (options.service_worker) {
|
|
356
|
+
const opts = __SVELTEKIT_DEV__ ? `, { type: 'module' }` : '';
|
|
357
|
+
|
|
358
|
+
// we use an anonymous function instead of an arrow function to support
|
|
359
|
+
// older browsers (https://github.com/sveltejs/kit/pull/5417)
|
|
360
|
+
blocks.push(`if ('serviceWorker' in navigator) {
|
|
361
|
+
addEventListener('load', function () {
|
|
362
|
+
navigator.serviceWorker.register('${prefixed('service-worker.js')}'${opts});
|
|
363
|
+
});
|
|
364
|
+
}`);
|
|
337
365
|
}
|
|
338
366
|
|
|
339
|
-
const
|
|
340
|
-
|
|
367
|
+
const init_app = `
|
|
368
|
+
{
|
|
369
|
+
${blocks.join('\n\n\t\t\t\t\t')}
|
|
370
|
+
}
|
|
371
|
+
`;
|
|
341
372
|
csp.add_script(init_app);
|
|
342
373
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
body += `\n\t\t<script ${attributes.join(' ')}>${init_app}</script>`;
|
|
374
|
+
body += `\n\t\t\t<script${
|
|
375
|
+
csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''
|
|
376
|
+
}>${init_app}</script>\n\t\t`;
|
|
348
377
|
}
|
|
349
378
|
|
|
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
|
-
}
|
|
379
|
+
const headers = new Headers({
|
|
380
|
+
'x-sveltekit-page': 'true',
|
|
381
|
+
'content-type': 'text/html'
|
|
382
|
+
});
|
|
377
383
|
|
|
378
384
|
if (state.prerendering) {
|
|
379
385
|
// TODO read headers set with setHeaders and convert into http-equiv where possible
|
|
@@ -391,6 +397,19 @@ export async function render_response({
|
|
|
391
397
|
if (http_equiv.length > 0) {
|
|
392
398
|
head = http_equiv.join('\n') + head;
|
|
393
399
|
}
|
|
400
|
+
} else {
|
|
401
|
+
const csp_header = csp.csp_provider.get_header();
|
|
402
|
+
if (csp_header) {
|
|
403
|
+
headers.set('content-security-policy', csp_header);
|
|
404
|
+
}
|
|
405
|
+
const report_only_header = csp.report_only_provider.get_header();
|
|
406
|
+
if (report_only_header) {
|
|
407
|
+
headers.set('content-security-policy-report-only', report_only_header);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (link_header_preloads.size) {
|
|
411
|
+
headers.set('link', Array.from(link_header_preloads).join(', '));
|
|
412
|
+
}
|
|
394
413
|
}
|
|
395
414
|
|
|
396
415
|
// add the content after the script/css links so the link elements are parsed first
|
|
@@ -411,39 +430,124 @@ export async function render_response({
|
|
|
411
430
|
done: true
|
|
412
431
|
})) || '';
|
|
413
432
|
|
|
414
|
-
if (
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
)
|
|
433
|
+
if (!chunks) {
|
|
434
|
+
headers.set('etag', `"${hash(transformed)}"`);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (DEV) {
|
|
438
|
+
if (page_config.csr) {
|
|
439
|
+
if (transformed.split('<!--').length < html.split('<!--').length) {
|
|
440
|
+
// the \u001B stuff is ANSI codes, so that we don't need to add a library to the runtime
|
|
441
|
+
// https://svelte.dev/repl/1b3f49696f0c44c881c34587f2537aa2
|
|
442
|
+
console.warn(
|
|
443
|
+
"\u001B[1m\u001B[31mRemoving comments in transformPageChunk can break Svelte's hydration\u001B[39m\u001B[22m"
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
} else {
|
|
447
|
+
if (chunks) {
|
|
448
|
+
console.warn(
|
|
449
|
+
'\u001B[1m\u001B[31mReturning promises from server `load` functions will only work if `csr === true`\u001B[39m\u001B[22m'
|
|
450
|
+
);
|
|
451
|
+
}
|
|
421
452
|
}
|
|
422
453
|
}
|
|
423
454
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
455
|
+
return !chunks
|
|
456
|
+
? text(transformed, {
|
|
457
|
+
status,
|
|
458
|
+
headers
|
|
459
|
+
})
|
|
460
|
+
: new Response(
|
|
461
|
+
new ReadableStream({
|
|
462
|
+
async start(controller) {
|
|
463
|
+
controller.enqueue(encoder.encode(transformed));
|
|
464
|
+
for await (const chunk of chunks) {
|
|
465
|
+
controller.enqueue(encoder.encode(chunk));
|
|
466
|
+
}
|
|
467
|
+
controller.close();
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
type: 'bytes'
|
|
471
|
+
}),
|
|
472
|
+
{
|
|
473
|
+
headers: {
|
|
474
|
+
'content-type': 'text/html'
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
);
|
|
478
|
+
}
|
|
429
479
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
480
|
+
/**
|
|
481
|
+
* If the serialized data contains promises, `chunks` will be an
|
|
482
|
+
* async iterable containing their resolutions
|
|
483
|
+
* @param {import('types').RequestEvent} event
|
|
484
|
+
* @param {import('types').SSROptions} options
|
|
485
|
+
* @param {Array<import('types').ServerDataNode | null>} nodes
|
|
486
|
+
* @param {string} global
|
|
487
|
+
* @returns {{ data: string, chunks: AsyncIterable<string> | null }}
|
|
488
|
+
*/
|
|
489
|
+
function get_data(event, options, nodes, global) {
|
|
490
|
+
let promise_id = 1;
|
|
491
|
+
let count = 0;
|
|
492
|
+
|
|
493
|
+
const { iterator, push, done } = create_async_iterator();
|
|
494
|
+
|
|
495
|
+
/** @param {any} thing */
|
|
496
|
+
function replacer(thing) {
|
|
497
|
+
if (typeof thing?.then === 'function') {
|
|
498
|
+
const id = promise_id++;
|
|
499
|
+
count += 1;
|
|
500
|
+
|
|
501
|
+
thing
|
|
502
|
+
.then(/** @param {any} data */ (data) => ({ data }))
|
|
503
|
+
.catch(
|
|
504
|
+
/** @param {any} error */ async (error) => ({
|
|
505
|
+
error: await handle_error_and_jsonify(event, options, error)
|
|
506
|
+
})
|
|
507
|
+
)
|
|
508
|
+
.then(
|
|
509
|
+
/**
|
|
510
|
+
* @param {{data: any; error: any}} result
|
|
511
|
+
*/
|
|
512
|
+
async ({ data, error }) => {
|
|
513
|
+
count -= 1;
|
|
514
|
+
|
|
515
|
+
let str;
|
|
516
|
+
try {
|
|
517
|
+
str = devalue.uneval({ id, data, error }, replacer);
|
|
518
|
+
} catch (e) {
|
|
519
|
+
error = await handle_error_and_jsonify(
|
|
520
|
+
event,
|
|
521
|
+
options,
|
|
522
|
+
new Error(`Failed to serialize promise while rendering ${event.route.id}`)
|
|
523
|
+
);
|
|
524
|
+
data = undefined;
|
|
525
|
+
str = devalue.uneval({ id, data, error }, replacer);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
push(`\n<script>${global}.resolve(${str})</script>`);
|
|
529
|
+
if (count === 0) done();
|
|
530
|
+
}
|
|
531
|
+
);
|
|
439
532
|
|
|
440
|
-
|
|
441
|
-
headers.set('link', Array.from(link_header_preloads).join(', '));
|
|
533
|
+
return `${global}.defer(${id})`;
|
|
442
534
|
}
|
|
443
535
|
}
|
|
444
536
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
537
|
+
try {
|
|
538
|
+
const strings = nodes.map((node) => {
|
|
539
|
+
if (!node) return 'null';
|
|
540
|
+
|
|
541
|
+
return `{"type":"data","data":${devalue.uneval(node.data, replacer)},${stringify_uses(node)}${
|
|
542
|
+
node.slash ? `,"slash":${JSON.stringify(node.slash)}` : ''
|
|
543
|
+
}}`;
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
return {
|
|
547
|
+
data: `[${strings.join(',')}]`,
|
|
548
|
+
chunks: count > 0 ? iterator : null
|
|
549
|
+
};
|
|
550
|
+
} catch (e) {
|
|
551
|
+
throw new Error(clarify_devalue_error(event, /** @type {any} */ (e)));
|
|
552
|
+
}
|
|
449
553
|
}
|
|
@@ -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';
|