Skip to content

Commit

Permalink
refactor: simplify escape_json_in_html util (#3804)
Browse files Browse the repository at this point in the history
  • Loading branch information
dominikg authored Feb 13, 2022
1 parent 6ca5c2a commit 0c6bc25
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 37 deletions.
5 changes: 5 additions & 0 deletions .changeset/metal-moons-provide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

refactor: use one escape function for json in html script body instead of two slightly different
14 changes: 12 additions & 2 deletions packages/kit/src/runtime/server/page/load_node.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { normalize } from '../../load.js';
import { respond } from '../index.js';
import { s } from '../../../utils/misc.js';
import { escape_json_value_in_html } from '../../../utils/escape.js';
import { escape_json_in_html } from '../../../utils/escape.js';
import { is_root_relative, resolve } from '../../../utils/url.js';
import { create_prerendering_url_proxy } from './utils.js';
import { is_pojo, lowercase_keys } from '../utils.js';
Expand Down Expand Up @@ -251,11 +251,21 @@ export async function load_node({
}

if (!opts.body || typeof opts.body === 'string') {
// the json constructed below is later added to the dom in a script tag
// make sure the used values are safe
const status_number = Number(response.status);
if (isNaN(status_number)) {
throw new Error(
`response.status is not a number. value: "${
response.status
}" type: ${typeof response.status}`
);
}
// prettier-ignore
fetched.push({
url: requested,
body: /** @type {string} */ (opts.body),
json: `{"status":${response.status},"statusText":${s(response.statusText)},"headers":${s(headers)},"body":"${escape_json_value_in_html(body)}"}`
json: `{"status":${status_number},"statusText":${s(response.statusText)},"headers":${s(headers)},"body":${escape_json_in_html(body)}}`
});
}

Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/runtime/server/page/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ export async function render_response({

if (shadow_props) {
// prettier-ignore
body += `<script type="application/json" data-type="svelte-props">${escape_json_in_html(s(shadow_props))}</script>`;
body += `<script type="application/json" data-type="svelte-props">${escape_json_in_html(shadow_props)}</script>`;
}
}

Expand Down
45 changes: 11 additions & 34 deletions packages/kit/src/utils/escape.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,26 @@
// dict from https://github.com/yahoo/serialize-javascript/blob/183c18a776e4635a379fdc620f81771f219832bb/index.js#L25
/** @type {Record<string, string>} */
const escape_json_in_html_dict = {
'&': '\\u0026',
'>': '\\u003e',
'<': '\\u003c',
'\u2028': '\\u2028',
'\u2029': '\\u2029'
};

/** @type {Record<string, string>} */
const escape_json_value_in_html_dict = {
'"': '\\"',
'<': '\\u003C',
'>': '\\u003E',
'/': '\\u002F',
'\\': '\\\\',
'\b': '\\b',
'\f': '\\f',
'\n': '\\n',
'\r': '\\r',
'\t': '\\t',
'\0': '\\0',
'\u2028': '\\u2028',
'\u2029': '\\u2029'
};

/**
* Escape a stringified JSON object that's going to be embedded in a `<script>` tag
* @param {string} str
*/
export function escape_json_in_html(str) {
// adapted from https://github.com/vercel/next.js/blob/694407450638b037673c6d714bfe4126aeded740/packages/next/server/htmlescape.ts
// based on https://github.com/zertosh/htmlescape
// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE
return str.replace(/[&><\u2028\u2029]/g, (match) => escape_json_in_html_dict[match]);
}
const escape_json_in_html_regex = new RegExp(
`[${Object.keys(escape_json_in_html_dict).join('')}]`,
'g'
);

/**
* Escape a string JSON value to be embedded into a `<script>` tag
* @param {string} str
* Escape a JSONValue that's going to be embedded in a `<script>` tag
* @param {import("@sveltejs/kit/types/helper").JSONValue} val
*/
export function escape_json_value_in_html(str) {
return escape(
str,
escape_json_value_in_html_dict,
(code) => `\\u${code.toString(16).toUpperCase()}`
export function escape_json_in_html(val) {
return JSON.stringify(val).replace(
escape_json_in_html_regex,
(match) => escape_json_in_html_dict[match]
);
}

Expand Down

0 comments on commit 0c6bc25

Please sign in to comment.