-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Description
Describe the problem
Consider the following REPL:
<script>
const myMap = new Map();
myMap.set('key1', 'value1');
myMap.set('key2', 'value2');
myMap.set('key3', 'value3');
for(const [k, v] of myMap.entries()){
// Javascript lets us iterate through a set so I don't see why svelte shouldn't
console.log(k, v);
}
</script>
<!-- This is not allowed because mySet does not have `.length` -->
{#each myMap.entries() as [k, v]}
<h1>Key: {k}, value: {v}</h1>
{/each}
Obviously one could trivially achieve this by wrapping myMap in Array.from
or spreading into an array but it would be nice if svelte could handle iterables in addition to ArrayLike
objects.
Describe the proposed solution
The problem is caused by the fact that svelte generates the code which loops over the each value using its .length
property. This can be seen below:
// from top of create_fragment
let each_1_anchor;
let each_value = /*myMap*/ ctx[0].entries;
let each_blocks = [];
for (let i = 0; i < each_value.length; i += 1) {
each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i));
}
and
// from the p function returned by create_fragment
for (i = 0; i < each_value.length; i += 1) {
const child_ctx = get_each_context(ctx, each_value, i);
if (each_blocks[i]) {
each_blocks[i].p(child_ctx, dirty);
} else {
each_blocks[i] = create_each_block(child_ctx);
each_blocks[i].c();
each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor);
}
}
As you can see it assumes that each_value
(in this case myMap
) has a length
property. It seems to me that we could easily generate the following code which is equivalent:
// the top of create_fragment
let each_1_anchor;
let each_value = /*myMap*/ ctx[0].entries();
let each_blocks = [];
for (const item of each_value) {
each_blocks.append(create_each_block(get_each_context(ctx, item)));
}
// the loop in the p function generated by create_fragment
let each_value_i = 0;
for (const item of each_value) {
const child_ctx = get_each_context(ctx, item);
if (each_blocks[each_value_i]) {
each_blocks[each_value_i].p(child_ctx, dirty);
} else {
each_blocks[each_value_i] = create_each_block(child_ctx);
each_blocks[each_value_i].c();
each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor);
}
}
With get_each_context
rewritten to be:
function get_each_context(ctx, item) {
const child_ctx = ctx.slice();
child_ctx[1] = item;
return child_ctx;
}
It's worth noting that the for...of
syntax isn't supported by IE11 but the svelte compiler appears to recommend the use of the spread operator which also isn't supported in IE11 so perhaps this isn't an issue.
Alternatives considered
The alternative here is to leave it as it is and tell people to use Array.from
and or [...mySet]
. It isn't the end of the world.
But given that it seems so trivial to support arbitrary iterables and that javascript can loop over iterables I can't see why svelte wouldn't implement this. Is it because you want any infinite loops to be in user code rather than generated code?
Importance
nice to have