chore: check namespace inside set attributes (#15443)

* set_attributes for <svelte:element>

* changeset

* Update .changeset/wise-hats-wonder.md

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>

* add changeset

* remove `chore` changeset — no need for non-user-facing changes to appear in changelog

* Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js

* determine element traits inside set_attributes

* unused

* stash lookup

---------

Co-authored-by: adiguba <frederic.martini@gmail.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
pull/15453/head
Rich Harris 6 months ago committed by GitHub
parent 43ff3047ac
commit 2d3818463a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: respect `svelte-ignore hydration_attribute_changed` on elements with spread attributes

@ -221,16 +221,7 @@ export function RegularElement(node, context) {
if (has_spread) {
const attributes_id = b.id(context.state.scope.generate('attributes'));
build_set_attributes(
attributes,
class_directives,
context,
node,
node_id,
attributes_id,
(node.metadata.svg || node.metadata.mathml || is_custom_element_node(node)) && b.true,
is_custom_element_node(node) && b.true
);
build_set_attributes(attributes, class_directives, context, node, node_id, attributes_id);
// If value binding exists, that one takes care of calling $.init_select
if (node.name === 'select' && !bindings.has('value')) {

@ -114,9 +114,7 @@ export function SvelteElement(node, context) {
inner_context,
node,
element_id,
attributes_id,
b.binary('===', b.member(element_id, 'namespaceURI'), b.id('$.NAMESPACE_SVG')),
b.call(b.member(b.member(element_id, 'nodeName'), 'includes'), b.literal('-'))
attributes_id
);
}

@ -17,8 +17,6 @@ import { build_template_chunk, get_expression_id } from './utils.js';
* @param {AST.RegularElement | AST.SvelteElement} element
* @param {Identifier} element_id
* @param {Identifier} attributes_id
* @param {false | Expression} preserve_attribute_case
* @param {false | Expression} is_custom_element
*/
export function build_set_attributes(
attributes,
@ -26,9 +24,7 @@ export function build_set_attributes(
context,
element,
element_id,
attributes_id,
preserve_attribute_case,
is_custom_element
attributes_id
) {
let is_dynamic = false;
@ -91,8 +87,6 @@ export function build_set_attributes(
element.metadata.scoped &&
context.state.analysis.css.hash !== '' &&
b.literal(context.state.analysis.css.hash),
preserve_attribute_case,
is_custom_element,
is_ignored(element, 'hydration_attribute_changed') && b.true
);

@ -33,6 +33,7 @@ export const UNINITIALIZED = Symbol();
export const FILENAME = Symbol('filename');
export const HMR = Symbol('hmr');
export const NAMESPACE_HTML = 'http://www.w3.org/1999/xhtml';
export const NAMESPACE_SVG = 'http://www.w3.org/2000/svg';
export const NAMESPACE_MATHML = 'http://www.w3.org/1998/Math/MathML';

@ -15,10 +15,14 @@ import {
} from '../../runtime.js';
import { clsx } from '../../../shared/attributes.js';
import { set_class } from './class.js';
import { NAMESPACE_HTML } from '../../../../constants.js';
export const CLASS = Symbol('class');
export const STYLE = Symbol('style');
const IS_CUSTOM_ELEMENT = Symbol('is custom element');
const IS_HTML = Symbol('is html');
/**
* The value/checked attribute in the template actually corresponds to the defaultValue property, so we need
* to remove it upon hydration to avoid a bug when someone resets the form value.
@ -63,8 +67,7 @@ export function remove_input_defaults(input) {
* @param {any} value
*/
export function set_value(element, value) {
// @ts-expect-error
var attributes = (element.__attributes ??= {});
var attributes = get_attributes(element);
if (
attributes.value ===
@ -87,8 +90,7 @@ export function set_value(element, value) {
* @param {boolean} checked
*/
export function set_checked(element, checked) {
// @ts-expect-error
var attributes = (element.__attributes ??= {});
var attributes = get_attributes(element);
if (
attributes.checked ===
@ -151,8 +153,7 @@ export function set_default_value(element, value) {
* @param {boolean} [skip_warning]
*/
export function set_attribute(element, attribute, value, skip_warning) {
// @ts-expect-error
var attributes = (element.__attributes ??= {});
var attributes = get_attributes(element);
if (hydrating) {
attributes[attribute] = element.getAttribute(attribute);
@ -261,20 +262,15 @@ export function set_custom_element_data(node, prop, value) {
* @param {Record<string | symbol, any> | undefined} prev
* @param {Record<string | symbol, any>} next New attributes - this function mutates this object
* @param {string} [css_hash]
* @param {boolean} [preserve_attribute_case]
* @param {boolean} [is_custom_element]
* @param {boolean} [skip_warning]
* @returns {Record<string, any>}
*/
export function set_attributes(
element,
prev,
next,
css_hash,
preserve_attribute_case = false,
is_custom_element = false,
skip_warning = false
) {
export function set_attributes(element, prev, next, css_hash, skip_warning = false) {
var attributes = get_attributes(element);
var is_custom_element = attributes[IS_CUSTOM_ELEMENT];
var preserve_attribute_case = !attributes[IS_HTML];
// If we're hydrating but the custom element is from Svelte, and it already scaffolded,
// then it might run block logic in hydration mode, which we have to prevent.
let is_hydrating_custom_element = hydrating && is_custom_element;
@ -299,9 +295,6 @@ export function set_attributes(
var setters = get_setters(element);
// @ts-expect-error
var attributes = /** @type {Record<string, unknown>} **/ (element.__attributes ??= {});
// since key is captured we use const
for (const key in next) {
// let instead of var because referenced in a closure
@ -432,7 +425,7 @@ export function set_attributes(
// @ts-ignore
element[name] = value;
} else if (typeof value !== 'function') {
set_attribute(element, name, value);
set_attribute(element, name, value, skip_warning);
}
}
if (key === 'style' && '__styles' in element) {
@ -448,6 +441,20 @@ export function set_attributes(
return current;
}
/**
*
* @param {Element} element
*/
function get_attributes(element) {
return /** @type {Record<string | symbol, unknown>} **/ (
// @ts-expect-error
element.__attributes ??= {
[IS_CUSTOM_ELEMENT]: element.nodeName.includes('-'),
[IS_HTML]: element.namespaceURI === NAMESPACE_HTML
}
);
}
/** @type {Map<string, string[]>} */
var setters_cache = new Map();

Loading…
Cancel
Save