blockless
Rich Harris 2 years ago
commit 38f5911a3f

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: adjust keyed each block equality handling

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: improve indexed each equality

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: prevent snippet children conflict

@ -296,7 +296,9 @@ const slots = {
/** @param {string} name @param {string} component */
'duplicate-slot-name': (name, component) => `Duplicate slot name '${name}' in <${component}>`,
'invalid-default-slot-content': () =>
`Found default slot content alongside an explicit slot="default"`
`Found default slot content alongside an explicit slot="default"`,
'conflicting-children-snippet': () =>
`Cannot use explicit children snippet at the same time as implicit children content. Remove either the non-whitespace content or the children snippet block`
};
/** @satisfies {Errors} */

@ -521,6 +521,24 @@ const validation = {
);
}
},
SnippetBlock(node, { path }) {
if (node.expression.name !== 'children') return;
const parent = path.at(-2);
if (!parent) return;
if (
parent.type === 'Component' ||
parent.type === 'SvelteComponent' ||
parent.type === 'SvelteSelf'
) {
if (
parent.fragment.nodes.some(
(node) => node.type !== 'SnippetBlock' && (node.type !== 'Text' || node.data.trim())
)
) {
error(node, 'conflicting-children-snippet');
}
}
},
SvelteHead(node) {
const attribute = node.attributes[0];
if (attribute) {

@ -2393,7 +2393,7 @@ export const template_visitors = {
const binding = /** @type {import('#compiler').Binding} */ (context.state.scope.get(item.name));
binding.expression = (id) => {
const item_with_loc = with_loc(item, id);
return each_item_is_reactive ? b.call('$.unwrap', item_with_loc) : item_with_loc;
return b.call('$.unwrap', item_with_loc);
};
if (node.index) {
const index_binding = /** @type {import('#compiler').Binding} */ (
@ -2401,7 +2401,7 @@ export const template_visitors = {
);
index_binding.expression = (id) => {
const index_with_loc = with_loc(index, id);
return each_item_is_reactive ? b.call('$.unwrap', index_with_loc) : index_with_loc;
return b.call('$.unwrap', index_with_loc);
};
}

@ -1,8 +1,7 @@
import {
regex_ends_with_whitespaces,
regex_not_whitespace,
regex_starts_with_whitespaces,
regex_whitespaces_strict
regex_starts_with_whitespaces
} from '../patterns.js';
import * as b from '../../utils/builders.js';
import { walk } from 'zimmerframe';

@ -0,0 +1,10 @@
import { test } from '../../test';
export default test({
error: {
code: 'conflicting-children-snippet',
message:
'Cannot use explicit children snippet at the same time as implicit children content. Remove either the non-whitespace content or the children snippet block',
position: [320, 353]
}
});

@ -0,0 +1,25 @@
<!-- ok -->
<Button>
hello
</Button>
<Button>
{#snippet children()}hi{/snippet}
</Button>
<Button>
hello
{#snippet foo()}x{/snippet}
</Button>
<Button>
{#snippet children()}hi{/snippet}
{#snippet foo()}x{/snippet}
</Button>
<div>
hello
{#snippet children()}hi{/snippet}
</div>
<!-- invalid -->
<Button>
hello
{#snippet children()}hi{/snippet}
</Button>

@ -0,0 +1,21 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: `100\n<button>Update</button>`,
async test({ assert, target }) {
/**
* @type {{ click: () => void; }}
*/
let btn1;
[btn1] = target.querySelectorAll('button');
flushSync(() => {
btn1.click();
});
assert.htmlEqual(target.innerHTML, `1000\n<button>Update</button>`);
}
});

@ -0,0 +1,13 @@
<script>
import { writable } from "svelte/store";
const roomState = writable({
users: {"gary": { name: "gary", value: 100 }},
});
</script>
{#each Object.values($roomState.users) as user (user.name)}
{user.value}
{/each}
<button onclick={() => $roomState.users["gary"].value = 1000}>Update</button>

@ -0,0 +1,21 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: `1\n1\n1\n1\n<button>+</button>`,
async test({ assert, target }) {
/**
* @type {{ click: () => void; }}
*/
let btn1;
[btn1] = target.querySelectorAll('button');
flushSync(() => {
btn1.click();
});
assert.htmlEqual(target.innerHTML, `2\n2\n2\n2\n<button>+</button>`);
}
});

@ -0,0 +1,26 @@
<script>
import { writable } from "svelte/store";
let store = writable([{ value: 1 }]);
let storeDeeper = writable({ items: [{ value: 1 }] });
function increment() {
$store[0].value++;
$storeDeeper.items[0].value++;
}
</script>
{#each $store as item (item)}
{item.value}
{/each}
{#each $storeDeeper.items as item (item)}
{item.value}
{/each}
{#each $store as item}
{item.value}
{/each}
{#each $storeDeeper.items as item}
{item.value}
{/each}
<button onclick={increment}>+</button>

@ -1,11 +1,10 @@
import { describe, assert, it } from 'vitest';
import * as $ from '../../src/internal/client/runtime';
import { effect, render_effect, user_effect } from '../../src/internal/client/reactivity/effects';
import { source } from '../../src/internal/client/reactivity/sources';
import type { Derived } from '../../src/internal/client/reactivity/types';
import { source, set } from '../../src/internal/client/reactivity/sources';
import type { Derived } from '../../src/internal/client/types';
import { proxy } from '../../src/internal/client/proxy';
import { derived } from '../../src/internal/client/reactivity/deriveds';
import { set } from '../../src/internal/client/reactivity/sources';
/**
* @param runes runes mode

Loading…
Cancel
Save