chore: update upstream fixes to be custom renderer aware

svelte-custom-renderer
paoloricciuti 2 weeks ago
parent af6419bd15
commit d2ec9dbebf

@ -234,7 +234,11 @@ export function RegularElement(node, context) {
// `<select defaultValue>` needs the options to exist before it can mark one
// as selected, so it is handled after the children, alongside `value`
if (node.name === 'select' && get_attribute_name(node, attribute) === 'defaultValue') {
if (
!custom_renderer &&
node.name === 'select' &&
get_attribute_name(node, attribute) === 'defaultValue'
) {
continue;
}
@ -538,7 +542,7 @@ export function RegularElement(node, context) {
// deferred from the attribute loop above, so that the options it selects from
// have been created and had their values assigned
if (!has_spread && name === 'select') {
if (!custom_renderer && !has_spread && name === 'select') {
const default_value = /** @type {AST.Attribute[]} */ (attributes).find(
(attribute) => get_attribute_name(node, attribute) === 'defaultValue'
);

@ -30,7 +30,8 @@ export function build_attribute_effect(
) {
/** @type {ObjectExpression['properties']} */
const values = [];
const is_select = element.type === 'RegularElement' && element.name === 'select';
const is_select =
!custom_renderer && element.type === 'RegularElement' && element.name === 'select';
const memoizer = new Memoizer();

@ -1,5 +1,5 @@
import { hydrate_next, hydrating } from '../hydration.js';
import { create_element, create_text } from '../operations.js';
import { append_child, create_element, create_text, set_attribute } from '../operations.js';
import { append } from '../template.js';
/**
@ -18,13 +18,13 @@ export function slot(anchor, $$props, name, slot_props, fallback_fn) {
// Use the stored reference because the shadow root may be closed.
if ($$props.$$host?.$$shadowRoot) {
const element = create_element('slot');
if (name !== 'default') element.name = name;
if (name !== 'default') set_attribute(element, 'name', name);
append(anchor, element);
if (fallback_fn !== null) {
const fallback_anchor = create_text();
element.append(fallback_anchor);
append_child(element, fallback_anchor);
fallback_fn(fallback_anchor);
}

@ -111,9 +111,10 @@ export function set_value(element, value) {
(attributes.value =
// treat null and undefined the same for the initial value
value ?? undefined) ||
// @ts-expect-error
// `progress` elements always need their value set when it's `0`
(element.value === value && (value !== 0 || node_name(element) !== PROGRESS_TAG))
(current_renderer == null &&
/** @type {any} */ (element).value === value &&
(value !== 0 || node_name(element) !== PROGRESS_TAG))
) {
return;
}
@ -368,8 +369,7 @@ function set_attributes(
}
if (key === 'class') {
var is_html =
element.namespaceURI === 'http://www.w3.org/1999/xhtml' && current_renderer != null;
var is_html = current_renderer == null && element.namespaceURI === NAMESPACE_HTML;
set_class(element, is_html, value, css_hash, prev?.[CLASS], next[CLASS]);
current[key] = value;
current[CLASS] = next[CLASS];
@ -619,18 +619,19 @@ function get_attributes(element) {
return /** @type {Record<string | symbol, unknown>} **/ (
/** @type {any} */ (element)[ATTRIBUTES_CACHE] ??= {
[IS_CUSTOM_ELEMENT]: (node_name(element) ?? '').includes('-'),
[IS_HTML]: element.namespaceURI === NAMESPACE_HTML
[IS_HTML]: current_renderer == null && element.namespaceURI === NAMESPACE_HTML
}
);
}
/** @type {Map<string, Set<string>>} */
var setters_cache = new Map();
var empty_setters = new Set();
/** @param {Element} element */
function get_setters(element) {
// if we have a custom renderer we just skip the check all together
if (current_renderer) return [];
if (current_renderer) return empty_setters;
var cache_key = get_attribute(element, 'is') || (node_name(element) ?? '');
var setters = setters_cache.get(cache_key);
if (setters) return setters;

@ -5,6 +5,11 @@ import { is_array } from '../../../../shared/utils.js';
import * as w from '../../../warnings.js';
import { Batch, current_batch, previous_batch } from '../../../reactivity/batch.js';
import { async_mode_flag } from '../../../../flags/index.js';
import {
has_attribute,
remove_attribute,
set_attribute as set_attribute_op
} from '../../operations.js';
/**
* Sets the `selected` attribute on an option so form reset can restore it.
@ -13,9 +18,13 @@ import { async_mode_flag } from '../../../../flags/index.js';
*/
export function set_selected(option, selected) {
if (selected) {
if (!option.hasAttribute('selected')) option.setAttribute('selected', '');
// The selected option could've changed via user selection, and
// setting the value without this check would set it back.
if (!has_attribute(option, 'selected')) {
set_attribute_op(option, 'selected', '');
}
} else {
option.removeAttribute('selected');
remove_attribute(option, 'selected');
}
}

@ -9,9 +9,18 @@ export default test({
const button = target.children.find(
(/** @type {any} */ n) => n.type === 'element' && n.name === 'button'
);
const select = target.children.find(
(/** @type {any} */ n) => n.type === 'element' && n.name === 'select'
);
const selected_option = select.children.find(
(/** @type {any} */ n) =>
n.type === 'element' && n.name === 'option' && n.attributes['value'] === 'other'
);
assert.equal(inputs.length, 4);
assert.ok(button);
assert.ok(select);
assert.ok(selected_option);
// Input 1: direct defaultValue attribute
const input_direct_val = inputs[0];
@ -52,6 +61,9 @@ export default test({
'defaultChecked via spread should go through renderer.setAttribute, not element.defaultChecked'
);
assert.equal(select.attributes['defaultValue'], 'default_val');
assert.equal(selected_option.attributes['selected'], '');
// --- After update ---
dispatch_event(button, 'click');
flushSync();
@ -75,5 +87,8 @@ export default test({
undefined,
'updated defaultChecked=false via spread should go through renderer.removeAttribute'
);
assert.equal(select.attributes['defaultValue'], 'new_default');
assert.equal(selected_option.attributes['selected'], undefined);
}
});

@ -10,6 +10,11 @@
<input type="checkbox" checked defaultChecked={default_chk} />
<input type="checkbox" checked {...spread_chk} />
<select defaultValue={default_val}>
<option value="default_val">Default</option>
<option value="other" selected={default_chk}>Other</option>
</select>
<button onclick={() => {
default_val = "new_default";
default_chk = false;

Loading…
Cancel
Save