mirror of https://github.com/sveltejs/svelte
commit
b9ee487b58
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"svelte": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: refine css `:global()` selector checks in a compound selector
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"svelte": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
breaking: warn on slots and event handlers in runes mode, error on `<slot>` + `{@render ...}` tag usage in same component
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"svelte": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: fall back to component namespace when not statically determinable, add way to tell `<svelte:element>` the namespace at runtime
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"svelte": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: remove memory leak from bind:this
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: take outroing elements out of the flow when animating siblings
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"svelte": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: remove memory leak from retaining old DOM elements
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"svelte": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: add warning when using `$bindable` rune without calling it
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"svelte": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: allow inspect reactivity map, set, date
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"svelte": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: widen ownership when sub state is assigned to new state
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
error: {
|
||||||
|
code: 'conflicting-slot-usage',
|
||||||
|
message:
|
||||||
|
'Cannot use <slot> syntax and {@render ...} tags in the same component. Migrate towards {@render ...} tags completely.',
|
||||||
|
position: [71, 84]
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
<script>
|
||||||
|
let { children } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{@render children()}
|
||||||
|
<slot></slot>
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
test({ assert, target }) {
|
||||||
|
const path = target.querySelector('path');
|
||||||
|
|
||||||
|
assert.equal(path?.namespaceURI, 'http://www.w3.org/2000/svg');
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<svelte:options namespace="svg" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Svg from "./svg.svelte";
|
||||||
|
|
||||||
|
let tag = "path";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Svg>
|
||||||
|
<svelte:element this="{tag}" d="M21 12a9 9 0 1 1-6.219-8.56"/>
|
||||||
|
</Svg>
|
||||||
|
After Width: | Height: | Size: 24 B |
@ -0,0 +1,10 @@
|
|||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
async test({ assert, target }) {
|
||||||
|
assert.equal(target.querySelector('path')?.namespaceURI, 'http://www.w3.org/2000/svg');
|
||||||
|
|
||||||
|
await target.querySelector('button')?.click();
|
||||||
|
assert.equal(target.querySelector('div')?.namespaceURI, 'http://www.w3.org/1999/xhtml');
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<script>
|
||||||
|
let tag = $state('path');
|
||||||
|
let xmlns = $state('http://www.w3.org/2000/svg');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button onclick={() => {
|
||||||
|
tag = 'div';
|
||||||
|
xmlns = null;
|
||||||
|
}}>change</button>
|
||||||
|
|
||||||
|
<!-- wrapper necessary or else jsdom says this is always an xhtml namespace -->
|
||||||
|
<svg>
|
||||||
|
<svelte:element this={tag} xmlns={xmlns} />
|
||||||
|
</svg>
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
import { flushSync } from '../../../../src/index-client';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
html: `<button>add item</button><button>make span</button><button>reverse</button>`,
|
||||||
|
|
||||||
|
async test({ assert, target }) {
|
||||||
|
const [btn1, btn2, btn3] = target.querySelectorAll('button');
|
||||||
|
|
||||||
|
flushSync(() => {
|
||||||
|
btn1?.click();
|
||||||
|
btn1?.click();
|
||||||
|
btn1?.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`<button>add item</button><button>make span</button><button>reverse</button><div>Item 1</div><div>Item 2</div><div>Item 3</div>`
|
||||||
|
);
|
||||||
|
|
||||||
|
flushSync(() => {
|
||||||
|
btn2?.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`<button>add item</button><button>make span</button><button>reverse</button><span>Item 1</span><span>Item 2</span><span>Item 3</span>`
|
||||||
|
);
|
||||||
|
|
||||||
|
flushSync(() => {
|
||||||
|
btn3?.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`<button>add item</button><button>make span</button><button>reverse</button><span>Item 3</span><span>Item 2</span><span>Item 1</span>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
<script>
|
||||||
|
let items = $state([]);
|
||||||
|
|
||||||
|
function add_item() {
|
||||||
|
items.push({
|
||||||
|
id: items.length,
|
||||||
|
text: 'Item ' + (items.length + 1),
|
||||||
|
html: '<div>Item ' + (items.length + 1) + '</div>',
|
||||||
|
dom: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_span() {
|
||||||
|
items.forEach(item => {
|
||||||
|
item.html = item.html.replace(/div/g, 'span')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function reverse() {
|
||||||
|
items.reverse();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button on:click={add_item}>add item</button>
|
||||||
|
<button on:click={make_span}>make span</button>
|
||||||
|
<button on:click={reverse}>reverse</button>
|
||||||
|
|
||||||
|
{#each items as item (item.id)}
|
||||||
|
{@html item.html}
|
||||||
|
{/each}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
import { test } from '../../test';
|
||||||
|
import { flushSync } from 'svelte';
|
||||||
|
import { log } from './log';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
compileOptions: {
|
||||||
|
dev: true
|
||||||
|
},
|
||||||
|
before_test() {
|
||||||
|
log.length = 0;
|
||||||
|
},
|
||||||
|
async test({ assert, target }) {
|
||||||
|
const [in1, in2] = target.querySelectorAll('input');
|
||||||
|
const [b1, b2, b3] = target.querySelectorAll('button');
|
||||||
|
|
||||||
|
assert.deepEqual(log, [
|
||||||
|
{ label: 'map', type: 'init', values: [] },
|
||||||
|
{ label: 'set', type: 'init', values: [] },
|
||||||
|
{ label: 'date', type: 'init', values: 1712966400000 }
|
||||||
|
]);
|
||||||
|
log.length = 0;
|
||||||
|
|
||||||
|
flushSync(() => b1.click()); // map.set('key', 'value')
|
||||||
|
|
||||||
|
in1.value = 'name';
|
||||||
|
in2.value = 'Svelte';
|
||||||
|
in1.dispatchEvent(new window.Event('input', { bubbles: true }));
|
||||||
|
in2.dispatchEvent(new window.Event('input', { bubbles: true }));
|
||||||
|
flushSync(() => b1.click()); // map.set('name', 'Svelte')
|
||||||
|
|
||||||
|
in2.value = 'World';
|
||||||
|
in2.dispatchEvent(new window.Event('input', { bubbles: true }));
|
||||||
|
flushSync(() => b1.click()); // map.set('name', 'World')
|
||||||
|
flushSync(() => b1.click()); // map.set('name', 'World')
|
||||||
|
|
||||||
|
assert.deepEqual(log, [
|
||||||
|
{ label: 'map', type: 'update', values: [['key', 'value']] },
|
||||||
|
{
|
||||||
|
label: 'map',
|
||||||
|
type: 'update',
|
||||||
|
values: [
|
||||||
|
['key', 'value'],
|
||||||
|
['name', 'Svelte']
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'map',
|
||||||
|
type: 'update',
|
||||||
|
values: [
|
||||||
|
['key', 'value'],
|
||||||
|
['name', 'World']
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
log.length = 0;
|
||||||
|
|
||||||
|
flushSync(() => b2.click()); // set.add('name');
|
||||||
|
|
||||||
|
in1.value = 'Svelte';
|
||||||
|
in1.dispatchEvent(new window.Event('input', { bubbles: true }));
|
||||||
|
flushSync(() => b2.click()); // set.add('Svelte');
|
||||||
|
|
||||||
|
flushSync(() => b2.click()); // set.add('Svelte');
|
||||||
|
|
||||||
|
assert.deepEqual(log, [
|
||||||
|
{ label: 'set', type: 'update', values: ['name'] },
|
||||||
|
{ label: 'set', type: 'update', values: ['name', 'Svelte'] }
|
||||||
|
]);
|
||||||
|
log.length = 0;
|
||||||
|
|
||||||
|
flushSync(() => b3.click()); // date.minutes++
|
||||||
|
flushSync(() => b3.click()); // date.minutes++
|
||||||
|
flushSync(() => b3.click()); // date.minutes++
|
||||||
|
|
||||||
|
assert.deepEqual(log, [
|
||||||
|
{ label: 'date', type: 'update', values: 1712966460000 },
|
||||||
|
{ label: 'date', type: 'update', values: 1712966520000 },
|
||||||
|
{ label: 'date', type: 'update', values: 1712966580000 }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
/** @type {any[]} */
|
||||||
|
export const log = [];
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
<script>
|
||||||
|
import { Map, Set, Date } from 'svelte/reactivity';
|
||||||
|
import { log } from './log';
|
||||||
|
|
||||||
|
const map = new Map();
|
||||||
|
const set = new Set();
|
||||||
|
const date = new Date('2024-04-13 00:00:00+0000');
|
||||||
|
let key = $state('key');
|
||||||
|
let value = $state('value');
|
||||||
|
|
||||||
|
$inspect(map).with((type, map) => {
|
||||||
|
log.push({ label: 'map', type, values: [...map] });
|
||||||
|
});
|
||||||
|
$inspect(set).with((type, set) => {
|
||||||
|
log.push({ label: 'set', type, values: [...set] });
|
||||||
|
});
|
||||||
|
$inspect(date).with((type, date) => {
|
||||||
|
log.push({ label: 'date', type, values: date.getTime() });
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input bind:value={key} />
|
||||||
|
<input bind:value={value} />
|
||||||
|
|
||||||
|
<button on:click={() => map.set(key, value)}>map</button>
|
||||||
|
<button on:click={() => set.add(key)}>set</button>
|
||||||
|
<button on:click={() => date.setMinutes(date.getMinutes() + 1)}>date</button>
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
<script>
|
||||||
|
let { item } = $props();
|
||||||
|
|
||||||
|
function onclick() {
|
||||||
|
item.name = `${item.name} edited`
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>{item?.name}</div>
|
||||||
|
<button {onclick}>Then click here</button>
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
import { tick } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
/** @type {typeof console.warn} */
|
||||||
|
let warn;
|
||||||
|
|
||||||
|
/** @type {any[]} */
|
||||||
|
let warnings = [];
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
compileOptions: {
|
||||||
|
dev: true
|
||||||
|
},
|
||||||
|
|
||||||
|
before_test: () => {
|
||||||
|
warn = console.warn;
|
||||||
|
|
||||||
|
console.warn = (...args) => {
|
||||||
|
warnings.push(...args);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
after_test: () => {
|
||||||
|
console.warn = warn;
|
||||||
|
warnings = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
async test({ assert, target }) {
|
||||||
|
const [btn1, btn2] = target.querySelectorAll('button');
|
||||||
|
|
||||||
|
btn1.click();
|
||||||
|
await tick();
|
||||||
|
|
||||||
|
assert.deepEqual(warnings.length, 0);
|
||||||
|
|
||||||
|
btn2.click();
|
||||||
|
await tick();
|
||||||
|
|
||||||
|
assert.deepEqual(warnings.length, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
<script>
|
||||||
|
import Child from './Child.svelte';
|
||||||
|
|
||||||
|
let items = $state([{ id: "test", name: "this is a test"}, { id:"test2", name: "this is a second test"}]);
|
||||||
|
let found = $state();
|
||||||
|
|
||||||
|
function onclick() {
|
||||||
|
found = items.find(c => c.id === 'test2');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button {onclick}>First click here</button>
|
||||||
|
<Child item={found} />
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"code": "invalid-css-global-selector-list",
|
||||||
|
"message": ":global(...) must not contain type or universal selectors when used in a compound selector",
|
||||||
|
"start": {
|
||||||
|
"line": 20,
|
||||||
|
"column": 6
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 20,
|
||||||
|
"column": 17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
<style>
|
||||||
|
::foo:global([data-state='checked']) {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
::foo:global(.foo) {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
::foo:global(#foo) {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
::foo:global(::foo) {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
::foo:global(:foo) {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
:global(h1) {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
::foo:global(h1) {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h1>hello world</h1>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"code": "invalid-css-type-selector-placement",
|
||||||
|
"message": ":global(...) must not be followed with a type selector",
|
||||||
|
"start": {
|
||||||
|
"line": 17,
|
||||||
|
"column": 14
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 17,
|
||||||
|
"column": 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<style>
|
||||||
|
:global(.foo):foo {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
:global(.foo)::foo {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
:global(.foo).bar {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
:global(.foo)#baz {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
:global(.foo)[id] {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
:global(.foo)h1 {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h1 class="bar" id="baz">hello world</h1>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
<script>
|
||||||
|
function test() {
|
||||||
|
try {
|
||||||
|
throw new TypeError("oops1");
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
throw new TypeError("oops2");
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
</script>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({});
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
<script>
|
||||||
|
let { a = $bindable } = $props();
|
||||||
|
</script>
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"code": "invalid-bindable-declaration",
|
||||||
|
"message": "Bindable component properties are declared using $bindable() in runes mode. Did you forget to call the function?",
|
||||||
|
"start": {
|
||||||
|
"column": 7,
|
||||||
|
"line": 2
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"column": 20,
|
||||||
|
"line": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
<script>
|
||||||
|
let { foo } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- ok -->
|
||||||
|
<button onclick={foo}>click me</button>
|
||||||
|
<Button onclick={foo}>click me</Button>
|
||||||
|
<Button on:click={foo}>click me</Button>
|
||||||
|
|
||||||
|
<!-- warn -->
|
||||||
|
<slot></slot>
|
||||||
|
<slot name="foo"></slot>
|
||||||
|
<button on:click={foo}>click me</button>
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"code": "deprecated-slot-element",
|
||||||
|
"end": {
|
||||||
|
"column": 13,
|
||||||
|
"line": 11
|
||||||
|
},
|
||||||
|
"message": "Using <slot> to render parent content is deprecated. Use {@render ...} tags instead.",
|
||||||
|
"start": {
|
||||||
|
"column": 0,
|
||||||
|
"line": 11
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "deprecated-slot-element",
|
||||||
|
"end": {
|
||||||
|
"column": 24,
|
||||||
|
"line": 12
|
||||||
|
},
|
||||||
|
"message": "Using <slot> to render parent content is deprecated. Use {@render ...} tags instead.",
|
||||||
|
"start": {
|
||||||
|
"column": 0,
|
||||||
|
"line": 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "deprecated-event-handler",
|
||||||
|
"end": {
|
||||||
|
"column": 22,
|
||||||
|
"line": 13
|
||||||
|
},
|
||||||
|
"message": "Using on:click to listen to the click event is is deprecated. Use the event attribute onclick instead.",
|
||||||
|
"start": {
|
||||||
|
"column": 8,
|
||||||
|
"line": 13
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
Loading…
Reference in new issue