@sveltejs/kit 1.0.0-next.458 → 1.0.0-next.460
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 +1 -1
- package/src/runtime/client/fetcher.js +2 -2
- package/src/runtime/client/start.js +0 -2
- package/src/runtime/server/page/fetch.js +1 -0
- package/src/runtime/server/page/render.js +2 -20
- package/src/runtime/server/page/serialize_data.js +58 -0
- package/src/utils/escape.js +0 -58
- package/types/internal.d.ts +0 -4
package/package.json
CHANGED
|
@@ -44,10 +44,10 @@ if (import.meta.env.DEV) {
|
|
|
44
44
|
export function initial_fetch(resource, opts) {
|
|
45
45
|
const url = JSON.stringify(typeof resource === 'string' ? resource : resource.url);
|
|
46
46
|
|
|
47
|
-
let selector = `script[
|
|
47
|
+
let selector = `script[data-sveltekit-fetched][data-url=${url}]`;
|
|
48
48
|
|
|
49
49
|
if (opts && typeof opts.body === 'string') {
|
|
50
|
-
selector += `[
|
|
50
|
+
selector += `[data-hash="${hash(opts.body)}"]`;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
const script = document.querySelector(selector);
|
|
@@ -192,6 +192,7 @@ export function create_fetch({ event, options, state, route, prerender_default }
|
|
|
192
192
|
async function text() {
|
|
193
193
|
const body = await response.text();
|
|
194
194
|
|
|
195
|
+
// TODO just pass `response.headers`, for processing inside `serialize_data`
|
|
195
196
|
/** @type {import('types').ResponseHeaders} */
|
|
196
197
|
const headers = {};
|
|
197
198
|
for (const [key, value] of response.headers) {
|
|
@@ -2,7 +2,7 @@ import { devalue } from 'devalue';
|
|
|
2
2
|
import { readable, writable } from 'svelte/store';
|
|
3
3
|
import * as cookie from 'cookie';
|
|
4
4
|
import { hash } from '../../hash.js';
|
|
5
|
-
import {
|
|
5
|
+
import { serialize_data } from './serialize_data.js';
|
|
6
6
|
import { s } from '../../../utils/misc.js';
|
|
7
7
|
import { Csp } from './csp.js';
|
|
8
8
|
import { serialize_error } from '../utils.js';
|
|
@@ -284,25 +284,7 @@ export async function render_response({
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
if (page_config.ssr && page_config.csr) {
|
|
287
|
-
|
|
288
|
-
const serialized_data = [];
|
|
289
|
-
|
|
290
|
-
for (const { url, body, response } of fetched) {
|
|
291
|
-
serialized_data.push(
|
|
292
|
-
render_json_payload_script(
|
|
293
|
-
{ type: 'data', url, body: typeof body === 'string' ? hash(body) : undefined },
|
|
294
|
-
response
|
|
295
|
-
)
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (validation_errors) {
|
|
300
|
-
serialized_data.push(
|
|
301
|
-
render_json_payload_script({ type: 'validation_errors' }, validation_errors)
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
body += `\n\t${serialized_data.join('\n\t')}`;
|
|
287
|
+
body += `\n\t${fetched.map(serialize_data).join('\n\t')}`;
|
|
306
288
|
}
|
|
307
289
|
|
|
308
290
|
if (options.service_worker) {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { escape_html_attr } from '../../../utils/escape.js';
|
|
2
|
+
import { hash } from '../../hash.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Inside a script element, only `</script` and `<!--` hold special meaning to the HTML parser.
|
|
6
|
+
*
|
|
7
|
+
* The first closes the script element, so everything after is treated as raw HTML.
|
|
8
|
+
* The second disables further parsing until `-->`, so the script element might be unexpectedly
|
|
9
|
+
* kept open until until an unrelated HTML comment in the page.
|
|
10
|
+
*
|
|
11
|
+
* U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR are escaped for the sake of pre-2018
|
|
12
|
+
* browsers.
|
|
13
|
+
*
|
|
14
|
+
* @see tests for unsafe parsing examples.
|
|
15
|
+
* @see https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements
|
|
16
|
+
* @see https://html.spec.whatwg.org/multipage/syntax.html#cdata-rcdata-restrictions
|
|
17
|
+
* @see https://html.spec.whatwg.org/multipage/parsing.html#script-data-state
|
|
18
|
+
* @see https://html.spec.whatwg.org/multipage/parsing.html#script-data-double-escaped-state
|
|
19
|
+
* @see https://github.com/tc39/proposal-json-superset
|
|
20
|
+
* @type {Record<string, string>}
|
|
21
|
+
*/
|
|
22
|
+
const replacements = {
|
|
23
|
+
'<': '\\u003C',
|
|
24
|
+
'\u2028': '\\u2028',
|
|
25
|
+
'\u2029': '\\u2029'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const pattern = new RegExp(`[${Object.keys(replacements).join('')}]`, 'g');
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Generates a raw HTML string containing a safe script element carrying data and associated attributes.
|
|
32
|
+
*
|
|
33
|
+
* It escapes all the special characters needed to guarantee the element is unbroken, but care must
|
|
34
|
+
* be taken to ensure it is inserted in the document at an acceptable position for a script element,
|
|
35
|
+
* and that the resulting string isn't further modified.
|
|
36
|
+
*
|
|
37
|
+
* @param {import('./types.js').Fetched} fetched
|
|
38
|
+
* @returns {string} The raw HTML of a script element carrying the JSON payload.
|
|
39
|
+
* @example const html = serialize_data('/data.json', null, { foo: 'bar' });
|
|
40
|
+
*/
|
|
41
|
+
export function serialize_data(fetched) {
|
|
42
|
+
const safe_payload = JSON.stringify(fetched.response).replace(
|
|
43
|
+
pattern,
|
|
44
|
+
(match) => replacements[match]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const attrs = [
|
|
48
|
+
'type="application/json"',
|
|
49
|
+
'data-sveltekit-fetched',
|
|
50
|
+
`data-url=${escape_html_attr(fetched.url)}`
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
if (fetched.body) {
|
|
54
|
+
attrs.push(`data-hash=${escape_html_attr(hash(fetched.body))}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return `<script ${attrs.join(' ')}>${safe_payload}</script>`;
|
|
58
|
+
}
|
package/src/utils/escape.js
CHANGED
|
@@ -1,61 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Inside a script element, only `</script` and `<!--` hold special meaning to the HTML parser.
|
|
3
|
-
*
|
|
4
|
-
* The first closes the script element, so everything after is treated as raw HTML.
|
|
5
|
-
* The second disables further parsing until `-->`, so the script element might be unexpectedly
|
|
6
|
-
* kept open until until an unrelated HTML comment in the page.
|
|
7
|
-
*
|
|
8
|
-
* U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR are escaped for the sake of pre-2018
|
|
9
|
-
* browsers.
|
|
10
|
-
*
|
|
11
|
-
* @see tests for unsafe parsing examples.
|
|
12
|
-
* @see https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements
|
|
13
|
-
* @see https://html.spec.whatwg.org/multipage/syntax.html#cdata-rcdata-restrictions
|
|
14
|
-
* @see https://html.spec.whatwg.org/multipage/parsing.html#script-data-state
|
|
15
|
-
* @see https://html.spec.whatwg.org/multipage/parsing.html#script-data-double-escaped-state
|
|
16
|
-
* @see https://github.com/tc39/proposal-json-superset
|
|
17
|
-
* @type {Record<string, string>}
|
|
18
|
-
*/
|
|
19
|
-
const render_json_payload_script_dict = {
|
|
20
|
-
'<': '\\u003C',
|
|
21
|
-
'\u2028': '\\u2028',
|
|
22
|
-
'\u2029': '\\u2029'
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const render_json_payload_script_regex = new RegExp(
|
|
26
|
-
`[${Object.keys(render_json_payload_script_dict).join('')}]`,
|
|
27
|
-
'g'
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Generates a raw HTML string containing a safe script element carrying JSON data and associated attributes.
|
|
32
|
-
*
|
|
33
|
-
* It escapes all the special characters needed to guarantee the element is unbroken, but care must
|
|
34
|
-
* be taken to ensure it is inserted in the document at an acceptable position for a script element,
|
|
35
|
-
* and that the resulting string isn't further modified.
|
|
36
|
-
*
|
|
37
|
-
* Attribute names must be type-checked so we don't need to escape them.
|
|
38
|
-
*
|
|
39
|
-
* @param {import('types').PayloadScriptAttributes} attrs A list of attributes to be added to the element.
|
|
40
|
-
* @param {any} payload The data to be carried by the element. Must be serializable to JSON.
|
|
41
|
-
* @returns {string} The raw HTML of a script element carrying the JSON payload.
|
|
42
|
-
* @example const html = render_json_payload_script({ type: 'data', url: '/data.json' }, { foo: 'bar' });
|
|
43
|
-
*/
|
|
44
|
-
export function render_json_payload_script(attrs, payload) {
|
|
45
|
-
const safe_payload = JSON.stringify(payload).replace(
|
|
46
|
-
render_json_payload_script_regex,
|
|
47
|
-
(match) => render_json_payload_script_dict[match]
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
let safe_attrs = '';
|
|
51
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
52
|
-
if (value === undefined) continue;
|
|
53
|
-
safe_attrs += ` sveltekit:data-${key}=${escape_html_attr(value)}`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return `<script type="application/json"${safe_attrs}>${safe_payload}</script>`;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
1
|
/**
|
|
60
2
|
* When inside a double-quoted attribute value, only `&` and `"` hold special meaning.
|
|
61
3
|
* @see https://html.spec.whatwg.org/multipage/parsing.html#attribute-value-(double-quoted)-state
|
package/types/internal.d.ts
CHANGED
|
@@ -135,10 +135,6 @@ export interface PageNode {
|
|
|
135
135
|
child_pages?: PageNode[];
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
export type PayloadScriptAttributes =
|
|
139
|
-
| { type: 'data'; url: string; body?: string }
|
|
140
|
-
| { type: 'validation_errors' };
|
|
141
|
-
|
|
142
138
|
export interface PrerenderDependency {
|
|
143
139
|
response: Response;
|
|
144
140
|
body: null | string | Uint8Array;
|