pull/15961/head
Rich Harris 4 months ago
parent d5d9593940
commit 673151adce

@ -27,17 +27,21 @@ export function build_set_attributes(
element_id, element_id,
attributes_id attributes_id
) { ) {
let is_dynamic = false;
/** @type {ObjectExpression['properties']} */ /** @type {ObjectExpression['properties']} */
const values = []; const values = [];
/** @type {Expression[]} */
const expressions = [];
/** @param {Expression} value */
function memoize(value) {
return b.id(`$${expressions.push(value) - 1}`);
}
for (const attribute of attributes) { for (const attribute of attributes) {
if (attribute.type === 'Attribute') { if (attribute.type === 'Attribute') {
const { value, has_state } = build_attribute_value( const { value } = build_attribute_value(attribute.value, context, (value, metadata) =>
attribute.value, metadata.has_call ? memoize(value) : value
context,
(value, metadata) => (metadata.has_call ? get_expression_id(context.state, value) : value)
); );
if ( if (
@ -51,16 +55,11 @@ export function build_set_attributes(
} else { } else {
values.push(b.init(attribute.name, value)); values.push(b.init(attribute.name, value));
} }
is_dynamic ||= has_state;
} else { } else {
// objects could contain reactive getters -> play it safe and always assume spread attributes are reactive
is_dynamic = true;
let value = /** @type {Expression} */ (context.visit(attribute)); let value = /** @type {Expression} */ (context.visit(attribute));
if (attribute.metadata.expression.has_call) { if (attribute.metadata.expression.has_call) {
value = get_expression_id(context.state, value); value = memoize(value);
} }
values.push(b.spread(value)); values.push(b.spread(value));
@ -75,9 +74,6 @@ export function build_set_attributes(
build_class_directives_object(class_directives, context) build_class_directives_object(class_directives, context)
) )
); );
is_dynamic ||=
class_directives.find((directive) => directive.metadata.expression.has_state) !== null;
} }
if (style_directives.length) { if (style_directives.length) {
@ -88,29 +84,25 @@ export function build_set_attributes(
build_style_directives_object(style_directives, context) build_style_directives_object(style_directives, context)
) )
); );
is_dynamic ||= style_directives.some((directive) => directive.metadata.expression.has_state);
} }
const call = b.call( context.state.init.push(
'$.set_attributes', b.stmt(
element_id, b.call(
is_dynamic ? attributes_id : b.null, '$.set_attribute_effect',
b.object(values), element_id,
element.metadata.scoped && b.arrow(
context.state.analysis.css.hash !== '' && expressions.map((_, i) => b.id(`$${i}`)),
b.literal(context.state.analysis.css.hash), b.object(values)
is_ignored(element, 'hydration_attribute_changed') && b.true ),
expressions.length > 0 && b.array(expressions.map((expression) => b.thunk(expression))),
element.metadata.scoped &&
context.state.analysis.css.hash !== '' &&
b.literal(context.state.analysis.css.hash),
is_ignored(element, 'hydration_attribute_changed') && b.true
)
)
); );
if (is_dynamic) {
context.state.init.push(
b.let(attributes_id),
b.stmt(b.call('$.set_attribute_effect', element_id, b.thunk(b.object(values))))
);
} else {
context.state.init.push(b.stmt(call));
}
} }
/** /**

@ -10,6 +10,7 @@ import { is_capture_event, is_delegated, normalize_attribute } from '../../../..
import { import {
active_effect, active_effect,
active_reaction, active_reaction,
get,
set_active_effect, set_active_effect,
set_active_reaction set_active_reaction
} from '../../runtime.js'; } from '../../runtime.js';
@ -19,6 +20,7 @@ import { set_class } from './class.js';
import { set_style } from './style.js'; import { set_style } from './style.js';
import { ATTACHMENT_KEY, NAMESPACE_HTML } from '../../../../constants.js'; import { ATTACHMENT_KEY, NAMESPACE_HTML } from '../../../../constants.js';
import { block, branch, destroy_effect } from '../../reactivity/effects.js'; import { block, branch, destroy_effect } from '../../reactivity/effects.js';
import { derived } from '../../reactivity/deriveds.js';
export const CLASS = Symbol('class'); export const CLASS = Symbol('class');
export const STYLE = Symbol('style'); export const STYLE = Symbol('style');
@ -448,22 +450,32 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal
set_hydrating(true); set_hydrating(true);
} }
for (let symbol of Object.getOwnPropertySymbols(next)) { // for (let symbol of Object.getOwnPropertySymbols(next)) {
if (symbol.description === ATTACHMENT_KEY) { // if (symbol.description === ATTACHMENT_KEY) {
attach(element, () => next[symbol]); // attach(element, () => next[symbol]);
} // }
} // }
return current; return current;
} }
/** /**
* @param {Element & ElementCSSInlineStyle} element * @param {Element & ElementCSSInlineStyle} element
* @param {() => Record<string | symbol, any>} fn * @param {(...expressions: any) => Record<string | symbol, any>} fn
* @param {Array<() => any>} thunks
* @param {string} [css_hash] * @param {string} [css_hash]
* @param {boolean} [skip_warning] * @param {boolean} [skip_warning]
*/ */
export function set_attribute_effect(element, fn, css_hash, skip_warning = false) { export function set_attribute_effect(
element,
fn,
thunks = [],
css_hash,
skip_warning = false,
d = derived
) {
const deriveds = thunks.map(d);
/** @type {Record<string | symbol, any>} */ /** @type {Record<string | symbol, any>} */
var prev = {}; var prev = {};
@ -471,24 +483,9 @@ export function set_attribute_effect(element, fn, css_hash, skip_warning = false
var effects = {}; var effects = {};
block(() => { block(() => {
var next = fn(); var next = fn(...deriveds.map(get));
for (const key in prev) {
if (!next[key]) {
element.removeAttribute(key);
}
}
for (let symbol of Object.getOwnPropertySymbols(prev)) { set_attributes(element, prev, next, css_hash, skip_warning);
if (!next[symbol]) {
destroy_effect(effects[symbol]);
delete effects[symbol];
}
}
for (const key in next) {
set_attribute(element, key, next[key], skip_warning);
}
for (let symbol of Object.getOwnPropertySymbols(next)) { for (let symbol of Object.getOwnPropertySymbols(next)) {
if (symbol.description === ATTACHMENT_KEY && next[symbol] !== prev[symbol]) { if (symbol.description === ATTACHMENT_KEY && next[symbol] !== prev[symbol]) {

Loading…
Cancel
Save