From f855a0b770f0e327fe1c9b2f42c006c2dad45dee Mon Sep 17 00:00:00 2001 From: Elliott Johnson Date: Wed, 18 Feb 2026 12:02:46 -0700 Subject: [PATCH] fix: misc option escaping and backwards compatibility (#17741) ### Before submitting the PR, please make sure you do the following - [ ] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs - [ ] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`. - [ ] This message body should clearly illustrate what problems it solves. - [ ] Ideally, include a test that fails without this PR but passes with it. - [ ] If this PR changes code within `packages/svelte/src`, add a changeset (`npx changeset`). ### Tests and linting - [ ] Run the tests with `pnpm test` and lint the project with `pnpm lint` --- .changeset/big-planets-appear.md | 5 +++++ packages/svelte/src/internal/server/errors.js | 14 +++++++------- packages/svelte/src/internal/server/renderer.js | 8 ++++---- packages/svelte/src/internal/shared/attributes.js | 4 +++- packages/svelte/src/internal/shared/utils.js | 1 + .../samples/option-body-escaped/_expected.html | 2 +- .../samples/option-body-escaped/main.svelte | 5 ++++- 7 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 .changeset/big-planets-appear.md diff --git a/.changeset/big-planets-appear.md b/.changeset/big-planets-appear.md new file mode 100644 index 0000000000..b5bd640f5f --- /dev/null +++ b/.changeset/big-planets-appear.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: misc option escaping and backwards compatibility diff --git a/packages/svelte/src/internal/server/errors.js b/packages/svelte/src/internal/server/errors.js index 15f8b2174d..d1e594acfd 100644 --- a/packages/svelte/src/internal/server/errors.js +++ b/packages/svelte/src/internal/server/errors.js @@ -15,12 +15,11 @@ export function async_local_storage_unavailable() { } /** - * `` is not a valid element name — the element will not be rendered - * @param {string} tag + * Encountered asynchronous work while rendering synchronously. * @returns {never} */ -export function dynamic_element_invalid_tag(tag) { - const error = new Error(`dynamic_element_invalid_tag\n\`\` is not a valid element name — the element will not be rendered\nhttps://svelte.dev/e/dynamic_element_invalid_tag`); +export function await_invalid() { + const error = new Error(`await_invalid\nEncountered asynchronous work while rendering synchronously.\nhttps://svelte.dev/e/await_invalid`); error.name = 'Svelte error'; @@ -28,11 +27,12 @@ export function dynamic_element_invalid_tag(tag) { } /** - * Encountered asynchronous work while rendering synchronously. + * `` is not a valid element name — the element will not be rendered + * @param {string} tag * @returns {never} */ -export function await_invalid() { - const error = new Error(`await_invalid\nEncountered asynchronous work while rendering synchronously.\nhttps://svelte.dev/e/await_invalid`); +export function dynamic_element_invalid_tag(tag) { + const error = new Error(`dynamic_element_invalid_tag\n\`\` is not a valid element name — the element will not be rendered\nhttps://svelte.dev/e/dynamic_element_invalid_tag`); error.name = 'Svelte error'; diff --git a/packages/svelte/src/internal/server/renderer.js b/packages/svelte/src/internal/server/renderer.js index 7f9a922f33..610e9bb268 100644 --- a/packages/svelte/src/internal/server/renderer.js +++ b/packages/svelte/src/internal/server/renderer.js @@ -11,7 +11,7 @@ import { attributes } from './index.js'; import { get_render_context, with_render_context, init_render_context } from './render-context.js'; import { sha256 } from './crypto.js'; import * as devalue from 'devalue'; -import { noop } from '../shared/utils.js'; +import { has_own_property, noop } from '../shared/utils.js'; import { escape_html } from '../../escaping.js'; /** @typedef {'head' | 'body'} RendererType */ @@ -268,7 +268,7 @@ export class Renderer { * @param {{ head?: string, body: any }} content */ const close = (renderer, value, { head, body }) => { - if (Object.hasOwn(attrs, 'value')) { + if (has_own_property.call(attrs, 'value')) { value = attrs.value; } @@ -276,7 +276,7 @@ export class Renderer { renderer.#out.push(' selected=""'); } - renderer.#out.push(`>${escape_html(body)}${is_rich ? '' : ''}`); + renderer.#out.push(`>${body}${is_rich ? '' : ''}`); // super edge case, but may as well handle it if (head) { @@ -299,7 +299,7 @@ export class Renderer { } }); } else { - close(this, body, { body }); + close(this, body, { body: escape_html(body) }); } } diff --git a/packages/svelte/src/internal/shared/attributes.js b/packages/svelte/src/internal/shared/attributes.js index 21bfd3d5a8..487a40baf3 100644 --- a/packages/svelte/src/internal/shared/attributes.js +++ b/packages/svelte/src/internal/shared/attributes.js @@ -1,5 +1,6 @@ import { escape_html } from '../../escaping.js'; import { clsx as _clsx } from 'clsx'; +import { has_own_property } from './utils.js'; /** * `
` should be rendered as `
` and _not_ @@ -27,7 +28,8 @@ export function attr(name, value, is_boolean = false) { is_boolean = true; } if (value == null || (!value && is_boolean)) return ''; - const normalized = (Object.hasOwn(replacements, name) && replacements[name].get(value)) || value; + const normalized = + (has_own_property.call(replacements, name) && replacements[name].get(value)) || value; const assignment = is_boolean ? `=""` : `="${escape_html(normalized, true)}"`; return ` ${name}${assignment}`; } diff --git a/packages/svelte/src/internal/shared/utils.js b/packages/svelte/src/internal/shared/utils.js index 771f6b345c..c9cc2c2d78 100644 --- a/packages/svelte/src/internal/shared/utils.js +++ b/packages/svelte/src/internal/shared/utils.js @@ -12,6 +12,7 @@ export var object_prototype = Object.prototype; export var array_prototype = Array.prototype; export var get_prototype_of = Object.getPrototypeOf; export var is_extensible = Object.isExtensible; +export var has_own_property = Object.prototype.hasOwnProperty; /** * @param {any} thing diff --git a/packages/svelte/tests/server-side-rendering/samples/option-body-escaped/_expected.html b/packages/svelte/tests/server-side-rendering/samples/option-body-escaped/_expected.html index f1f609d095..0802f01083 100644 --- a/packages/svelte/tests/server-side-rendering/samples/option-body-escaped/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/option-body-escaped/_expected.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/option-body-escaped/main.svelte b/packages/svelte/tests/server-side-rendering/samples/option-body-escaped/main.svelte index ab28f8bda5..a2f96383d1 100644 --- a/packages/svelte/tests/server-side-rendering/samples/option-body-escaped/main.svelte +++ b/packages/svelte/tests/server-side-rendering/samples/option-body-escaped/main.svelte @@ -1,6 +1,9 @@