pull/12530/head
Rich Harris 2 years ago
parent 8feb671de1
commit 5988e521d6

@ -1002,43 +1002,44 @@ function serialize_inline_component(node, component_name, context, anchor = cont
* @returns * @returns
*/ */
function serialize_bind_this(bind_this, context, node) { function serialize_bind_this(bind_this, context, node) {
let i = 0;
/** @type {Map<string, [arg_idx: number, transformed: Expression, expression: Expression | ((id: Identifier) => Expression)]>} */
const each_ids = new Map();
const child_state = { const child_state = {
...context.state, ...context.state,
getters: { ...context.state.getters } getters: { ...context.state.getters }
}; };
// Transform each reference to an each block context variable into a $$value_<i> variable /** @type {Identifier[]} */
// by temporarily changing the `expression` of the corresponding binding. const ids = [];
// These $$value_<i> variables will be filled in by the bind_this runtime function through its last argument.
/** @type {Expression[]} */
const values = [];
/** @type {string[]} */
const seen = [];
// Pass in each context variables to the get/set functions, so that we can null out old values on teardown.
// Note that we only do this for each context variables, the consequence is that the value might be stale in // Note that we only do this for each context variables, the consequence is that the value might be stale in
// some scenarios where the value is a member expression with changing computed parts or using a combination of multiple // some scenarios where the value is a member expression with changing computed parts or using a combination of multiple
// variables, but that was the same case in Svelte 4, too. Once legacy mode is gone completely, we can revisit this. // variables, but that was the same case in Svelte 4, too. Once legacy mode is gone completely, we can revisit this.
walk(bind_this, null, { walk(bind_this, null, {
Identifier(node) { Identifier(node) {
const binding = child_state.scope.get(node.name); const binding = child_state.scope.get(node.name);
if (!binding || each_ids.has(node.name)) return; if (!binding || seen.includes(node.name)) return;
const associated_node = Array.from(child_state.scopes.entries()).find( const associated_node = Array.from(child_state.scopes.entries()).find(
([_, scope]) => scope === binding?.scope ([_, scope]) => scope === binding?.scope
)?.[0]; )?.[0];
if (associated_node?.type === 'EachBlock') { if (associated_node?.type === 'EachBlock') {
each_ids.set(node.name, [ seen.push(node.name);
i,
/** @type {Expression} */ (context.visit(node)), ids.push(node);
child_state.getters[node.name] values.push(/** @type {Expression} */ (context.visit(node)));
]); child_state.getters[node.name] = node;
child_state.getters[node.name] = b.id('$$value_' + i);
i++;
} }
} }
}); });
const bind_this_id = /** @type {Expression} */ (context.visit(bind_this, child_state)); const bind_this_id = /** @type {Expression} */ (context.visit(bind_this, child_state));
const ids = Array.from(each_ids.values()).map((id) => b.id('$$value_' + id[0]));
const assignment = /** @type {Expression} */ ( const assignment = /** @type {Expression} */ (
context.visit(b.assignment('=', bind_this, b.id('$$value')), child_state) context.visit(b.assignment('=', bind_this, b.id('$$value')), child_state)
); );
@ -1060,8 +1061,8 @@ function serialize_bind_this(bind_this, context, node) {
bind_node = bind_node.object; bind_node = bind_node.object;
} }
if (each_ids.size) { if (values.length > 0) {
args.push(b.thunk(b.array(Array.from(each_ids.values()).map((id) => id[1])))); args.push(b.thunk(b.array(values)));
} }
return b.call('$.bind_this', ...args); return b.call('$.bind_this', ...args);

Loading…
Cancel
Save