mirror of https://github.com/sveltejs/svelte
commit
b9b0235f50
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
feat: introduce `$host` rune, deprecate `createEventDispatcher`
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: execute sole static script tag
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: make static `element` property available for the SvelteComponent type
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: improve internal proxied state signal heuristic
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: keep sibling selectors when dealing with slots/render tags/`svelte:element` tags
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
breaking: robustify interop of exports and props in runes mode
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: ensure deep mutation ownership widening
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: improve compiled output of multiple call expression in single text node
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: improve hydration of svelte head blocks
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: improve handled of unowned derived signals
|
||||
@ -0,0 +1,34 @@
|
||||
import { walk } from 'zimmerframe';
|
||||
import { warn } from '../../../warnings.js';
|
||||
import { is_keyframes_node } from '../../css.js';
|
||||
|
||||
/**
|
||||
* @param {import('#compiler').Css.StyleSheet} stylesheet
|
||||
* @param {import('../../types.js').RawWarning[]} warnings
|
||||
*/
|
||||
export function warn_unused(stylesheet, warnings) {
|
||||
walk(stylesheet, { warnings, stylesheet }, visitors);
|
||||
}
|
||||
|
||||
/** @type {import('zimmerframe').Visitors<import('#compiler').Css.Node, { warnings: import('../../types.js').RawWarning[], stylesheet: import('#compiler').Css.StyleSheet }>} */
|
||||
const visitors = {
|
||||
Atrule(node, context) {
|
||||
if (!is_keyframes_node(node)) {
|
||||
context.next();
|
||||
}
|
||||
},
|
||||
PseudoClassSelector(node, context) {
|
||||
if (node.name === 'is' || node.name === 'where') {
|
||||
context.next();
|
||||
}
|
||||
},
|
||||
ComplexSelector(node, context) {
|
||||
if (!node.metadata.used) {
|
||||
const content = context.state.stylesheet.content;
|
||||
const text = content.styles.substring(node.start - content.start, node.end - content.start);
|
||||
warn(context.state.warnings, node, context.path, 'css-unused-selector', text);
|
||||
}
|
||||
|
||||
context.next();
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,8 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
error: {
|
||||
code: 'conflicting-property-name',
|
||||
message: 'Cannot have a property and a component export with the same name'
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,4 @@
|
||||
<script>
|
||||
let { x: y } = $props();
|
||||
export function x() {}
|
||||
</script>
|
||||
@ -0,0 +1,8 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
error: {
|
||||
code: 'invalid-host-location',
|
||||
message: '$host() can only be used inside custom element component instances'
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,3 @@
|
||||
<script>
|
||||
$host();
|
||||
</script>
|
||||
@ -0,0 +1 @@
|
||||
$host();
|
||||
@ -0,0 +1,20 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
warnings: [
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 44,
|
||||
column: 14,
|
||||
line: 4
|
||||
},
|
||||
message: 'Unused CSS selector "p[type=\'B\' s]"',
|
||||
start: {
|
||||
character: 31,
|
||||
column: 1,
|
||||
line: 4
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
@ -0,0 +1,20 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
warnings: [
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 33,
|
||||
column: 6,
|
||||
line: 6
|
||||
},
|
||||
message: 'Unused CSS selector "x y z"',
|
||||
start: {
|
||||
character: 28,
|
||||
column: 1,
|
||||
line: 6
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
@ -0,0 +1,20 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
warnings: [
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 479,
|
||||
column: 19,
|
||||
line: 22
|
||||
},
|
||||
message: 'Unused CSS selector ":global(.x) + .bar"',
|
||||
start: {
|
||||
character: 461,
|
||||
column: 1,
|
||||
line: 22
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
@ -0,0 +1,13 @@
|
||||
|
||||
.before.svelte-xyz + .foo:where(.svelte-xyz) { color: green; }
|
||||
.before.svelte-xyz ~ .foo:where(.svelte-xyz) { color: green; }
|
||||
.before.svelte-xyz ~ .bar:where(.svelte-xyz) { color: green; }
|
||||
|
||||
.x + .foo.svelte-xyz { color: green; }
|
||||
.x + .foo.svelte-xyz span:where(.svelte-xyz) { color: green; }
|
||||
.x ~ .foo.svelte-xyz { color: green; }
|
||||
.x ~ .foo.svelte-xyz span:where(.svelte-xyz) { color: green; }
|
||||
.x ~ .bar.svelte-xyz { color: green; }
|
||||
|
||||
/* no match */
|
||||
/* (unused) :global(.x) + .bar { color: green; }*/
|
||||
@ -0,0 +1,23 @@
|
||||
<div>
|
||||
<p class="before">before</p>
|
||||
{@render children()}
|
||||
<p class="foo">
|
||||
<span>foo</span>
|
||||
</p>
|
||||
<p class="bar">bar</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.before + .foo { color: green; }
|
||||
.before ~ .foo { color: green; }
|
||||
.before ~ .bar { color: green; }
|
||||
|
||||
:global(.x) + .foo { color: green; }
|
||||
:global(.x) + .foo span { color: green; }
|
||||
:global(.x) ~ .foo { color: green; }
|
||||
:global(.x) ~ .foo span { color: green; }
|
||||
:global(.x) ~ .bar { color: green; }
|
||||
|
||||
/* no match */
|
||||
:global(.x) + .bar { color: green; }
|
||||
</style>
|
||||
@ -0,0 +1,20 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
warnings: [
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 472,
|
||||
column: 19,
|
||||
line: 22
|
||||
},
|
||||
message: 'Unused CSS selector ":global(.x) + .bar"',
|
||||
start: {
|
||||
character: 454,
|
||||
column: 1,
|
||||
line: 22
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
@ -0,0 +1,13 @@
|
||||
|
||||
.before.svelte-xyz + .foo:where(.svelte-xyz) { color: green; }
|
||||
.before.svelte-xyz ~ .foo:where(.svelte-xyz) { color: green; }
|
||||
.before.svelte-xyz ~ .bar:where(.svelte-xyz) { color: green; }
|
||||
|
||||
.x + .foo.svelte-xyz { color: green; }
|
||||
.x + .foo.svelte-xyz span:where(.svelte-xyz) { color: green; }
|
||||
.x ~ .foo.svelte-xyz { color: green; }
|
||||
.x ~ .foo.svelte-xyz span:where(.svelte-xyz) { color: green; }
|
||||
.x ~ .bar.svelte-xyz { color: green; }
|
||||
|
||||
/* no match */
|
||||
/* (unused) :global(.x) + .bar { color: green; }*/
|
||||
@ -0,0 +1,23 @@
|
||||
<div>
|
||||
<p class="before">before</p>
|
||||
<slot></slot>
|
||||
<p class="foo">
|
||||
<span>foo</span>
|
||||
</p>
|
||||
<p class="bar">bar</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.before + .foo { color: green; }
|
||||
.before ~ .foo { color: green; }
|
||||
.before ~ .bar { color: green; }
|
||||
|
||||
:global(.x) + .foo { color: green; }
|
||||
:global(.x) + .foo span { color: green; }
|
||||
:global(.x) ~ .foo { color: green; }
|
||||
:global(.x) ~ .foo span { color: green; }
|
||||
:global(.x) ~ .bar { color: green; }
|
||||
|
||||
/* no match */
|
||||
:global(.x) + .bar { color: green; }
|
||||
</style>
|
||||
@ -0,0 +1,20 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
warnings: [
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 496,
|
||||
column: 10,
|
||||
line: 26
|
||||
},
|
||||
message: 'Unused CSS selector ".x + .bar"',
|
||||
start: {
|
||||
character: 487,
|
||||
column: 1,
|
||||
line: 26
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
@ -0,0 +1,13 @@
|
||||
|
||||
.before.svelte-xyz + .foo:where(.svelte-xyz) { color: green; }
|
||||
.before.svelte-xyz ~ .foo:where(.svelte-xyz) { color: green; }
|
||||
.before.svelte-xyz ~ .bar:where(.svelte-xyz) { color: green; }
|
||||
|
||||
.x.svelte-xyz + .foo:where(.svelte-xyz) { color: green; }
|
||||
.x.svelte-xyz + .foo:where(.svelte-xyz) span:where(.svelte-xyz) { color: green; }
|
||||
.x.svelte-xyz ~ .foo:where(.svelte-xyz) { color: green; }
|
||||
.x.svelte-xyz ~ .foo:where(.svelte-xyz) span:where(.svelte-xyz) { color: green; }
|
||||
.x.svelte-xyz ~ .bar:where(.svelte-xyz) { color: green; }
|
||||
|
||||
/* no match */
|
||||
/* (unused) .x + .bar { color: green; }*/
|
||||
@ -0,0 +1,27 @@
|
||||
<script>
|
||||
let tag = 'div'
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<p class="before">before</p>
|
||||
<svelte:element class="x" this={tag}></svelte:element>
|
||||
<p class="foo">
|
||||
<span>foo</span>
|
||||
</p>
|
||||
<p class="bar">bar</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.before + .foo { color: green; }
|
||||
.before ~ .foo { color: green; }
|
||||
.before ~ .bar { color: green; }
|
||||
|
||||
.x + .foo { color: green; }
|
||||
.x + .foo span { color: green; }
|
||||
.x ~ .foo { color: green; }
|
||||
.x ~ .foo span { color: green; }
|
||||
.x ~ .bar { color: green; }
|
||||
|
||||
/* no match */
|
||||
.x + .bar { color: green; }
|
||||
</style>
|
||||
@ -0,0 +1,20 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
warnings: [
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 38,
|
||||
column: 11,
|
||||
line: 6
|
||||
},
|
||||
message: 'Unused CSS selector "z"',
|
||||
start: {
|
||||
character: 37,
|
||||
column: 10,
|
||||
line: 6
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
@ -0,0 +1,104 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
warnings: [
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 239,
|
||||
column: 13,
|
||||
line: 20
|
||||
},
|
||||
message: 'Unused CSS selector ".unused"',
|
||||
start: {
|
||||
character: 232,
|
||||
column: 6,
|
||||
line: 20
|
||||
}
|
||||
},
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 302,
|
||||
column: 10,
|
||||
line: 27
|
||||
},
|
||||
message: 'Unused CSS selector ".unused"',
|
||||
start: {
|
||||
character: 295,
|
||||
column: 3,
|
||||
line: 27
|
||||
}
|
||||
},
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 328,
|
||||
column: 6,
|
||||
line: 30
|
||||
},
|
||||
message: 'Unused CSS selector ".c"',
|
||||
start: {
|
||||
character: 326,
|
||||
column: 4,
|
||||
line: 30
|
||||
}
|
||||
},
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 381,
|
||||
column: 10,
|
||||
line: 37
|
||||
},
|
||||
message: 'Unused CSS selector ".unused"',
|
||||
start: {
|
||||
character: 374,
|
||||
column: 3,
|
||||
line: 37
|
||||
}
|
||||
},
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 471,
|
||||
column: 7,
|
||||
line: 47
|
||||
},
|
||||
message: 'Unused CSS selector "& &"',
|
||||
start: {
|
||||
character: 468,
|
||||
column: 4,
|
||||
line: 47
|
||||
}
|
||||
},
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 634,
|
||||
column: 5,
|
||||
line: 66
|
||||
},
|
||||
message: 'Unused CSS selector "&.b"',
|
||||
start: {
|
||||
character: 631,
|
||||
column: 2,
|
||||
line: 66
|
||||
}
|
||||
},
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 666,
|
||||
column: 9,
|
||||
line: 70
|
||||
},
|
||||
message: 'Unused CSS selector ".unused"',
|
||||
start: {
|
||||
character: 659,
|
||||
column: 2,
|
||||
line: 70
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
@ -0,0 +1,20 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
warnings: [
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
end: {
|
||||
character: 32,
|
||||
column: 3,
|
||||
line: 5
|
||||
},
|
||||
message: 'Unused CSS selector "h2"',
|
||||
start: {
|
||||
character: 30,
|
||||
column: 1,
|
||||
line: 5
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
@ -0,0 +1,24 @@
|
||||
import { test } from '../../assert';
|
||||
const tick = () => Promise.resolve();
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
target.innerHTML = '<custom-element></custom-element>';
|
||||
/** @type {any} */
|
||||
const el = target.querySelector('custom-element');
|
||||
|
||||
/** @type {string[]} */
|
||||
const events = [];
|
||||
const handle_evt = (e) => events.push(e.type, e.detail);
|
||||
el.addEventListener('greeting', handle_evt);
|
||||
|
||||
await tick();
|
||||
|
||||
el.shadowRoot.querySelector('button').click();
|
||||
assert.deepEqual(events, ['greeting', 'hello']);
|
||||
|
||||
el.removeEventListener('greeting', handle_evt);
|
||||
el.shadowRoot.querySelector('button').click();
|
||||
assert.deepEqual(events, ['greeting', 'hello']);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,9 @@
|
||||
<svelte:options customElement="custom-element" />
|
||||
|
||||
<script>
|
||||
function greet(greeting) {
|
||||
$host().dispatchEvent(new CustomEvent('greeting', { detail: greeting }))
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={() => greet('hello')}>say hello</button>
|
||||
@ -1,7 +1,7 @@
|
||||
import { test } from '../../test';
|
||||
import { test } from '../../assert';
|
||||
|
||||
export default test({
|
||||
test({ assert, component, window }) {
|
||||
test({ assert, window }) {
|
||||
document.dispatchEvent(new Event('DOMContentLoaded'));
|
||||
assert.equal(window.document.querySelector('button')?.textContent, 'Hello world');
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
import { test } from '../../assert';
|
||||
|
||||
export default test({
|
||||
// Test that @html does not execute scripts when instantiated in the client.
|
||||
// Needs to be in this test suite because JSDOM does not quite get this right.
|
||||
mode: ['client'],
|
||||
test({ window, assert }) {
|
||||
// In here to give effects etc time to execute
|
||||
assert.htmlEqual(
|
||||
window.document.body.innerHTML,
|
||||
`<main>
|
||||
<div><script></script></div><script>document.body.innerHTML = 'this should not be executed'</script>
|
||||
<script></script><script>document.body.innerHTML = 'this neither'</script>
|
||||
</main>`
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,7 @@
|
||||
<div>
|
||||
<script></script>
|
||||
</div>
|
||||
{@html `<script>document.body.innerHTML = 'this should not be executed'</script>`}
|
||||
{#if true}
|
||||
<script></script>{@html `<script>document.body.innerHTML = 'this neither'</script>`}
|
||||
{/if}
|
||||
@ -0,0 +1,11 @@
|
||||
import { test } from '../../assert';
|
||||
|
||||
export default test({
|
||||
// Test that template with sole script tag does execute when instantiated in the client.
|
||||
// Needs to be in this test suite because JSDOM does not quite get this right.
|
||||
mode: ['client'],
|
||||
test({ window, assert }) {
|
||||
// In here to give effects etc time to execute
|
||||
assert.htmlEqual(window.document.body.innerHTML, 'this should be executed');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,4 @@
|
||||
<div></div>
|
||||
{#if true}
|
||||
<script>document.body.innerHTML = 'this should be executed'</script>
|
||||
{/if}
|
||||
@ -0,0 +1,12 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
// The test has a bunch of queueMicrotasks
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `<div>Zeeba Neighba</div>`);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,34 @@
|
||||
<script context="module">
|
||||
export class Thing {
|
||||
data = $state();
|
||||
|
||||
subscribe() {
|
||||
queueMicrotask(() => {
|
||||
this.data = {
|
||||
name: `Zeeba Neighba`,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
name = $derived(this.data?.name);
|
||||
}
|
||||
|
||||
export class Things {
|
||||
thing = $state();
|
||||
|
||||
subscribe() {
|
||||
queueMicrotask(() => {
|
||||
this.thing = new Thing();
|
||||
this.thing.subscribe();
|
||||
this.thing.name;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
let model = new Things();
|
||||
$effect(() => model.subscribe());
|
||||
</script>
|
||||
|
||||
<div>{model.thing?.name}</div>
|
||||
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
let { ...rest } = $props();
|
||||
let count = $state(0);
|
||||
export function increment() {
|
||||
count++;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- test that the binding isn't inside rest -->
|
||||
{Object.keys(rest).length}
|
||||
|
||||
{count}
|
||||
@ -0,0 +1,17 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
compileOptions: {
|
||||
dev: true // to ensure we don't throw a false-positive "cannot bind to this" error
|
||||
},
|
||||
html: `0 0 <button>increment</button>`,
|
||||
|
||||
async test({ assert, target }) {
|
||||
const btn = target.querySelector('button');
|
||||
|
||||
btn?.click();
|
||||
await Promise.resolve();
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `0 1 <button>increment</button>`);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
import Counter from './Counter.svelte';
|
||||
let increment;
|
||||
</script>
|
||||
|
||||
<Counter bind:increment={increment} />
|
||||
<button onclick={increment}>increment</button>
|
||||
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
let title = $state('Hello world');
|
||||
let desc = $state('Some description');
|
||||
</script>
|
||||
<svelte:head>
|
||||
<title>{title}</title>
|
||||
<meta name="description" content={desc}>
|
||||
<meta name="author" content="@svelteawesome">
|
||||
</svelte:head>
|
||||
@ -0,0 +1,12 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
html: `<div>Hello</div>`,
|
||||
|
||||
async test({ assert, target }) {
|
||||
assert.htmlEqual(
|
||||
target.ownerDocument.head.innerHTML,
|
||||
`<script async="" src="https://www.googletagmanager.com/gtag/js?id=12345"></script><meta content="Some description" name="description"><meta content="@svelteawesome" name="author"><title>Hello world</title>`
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
import MetaTag from './MetaTag.svelte'
|
||||
</script>
|
||||
<svelte:head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=12345"></script>
|
||||
</svelte:head>
|
||||
<MetaTag />
|
||||
|
||||
<div>Hello</div>
|
||||
@ -0,0 +1,35 @@
|
||||
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 btn = target.querySelector('button');
|
||||
|
||||
await btn?.click();
|
||||
await tick();
|
||||
assert.deepEqual(warnings.length, 0);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { setContext } from 'svelte';
|
||||
import Sub from './sub.svelte';
|
||||
|
||||
class Person1 {
|
||||
value = $state({ person: 'John', age: 33 })
|
||||
}
|
||||
const class_nested_state = $state(new Person1());
|
||||
|
||||
class Person2 {
|
||||
person = $state('John');
|
||||
age = $state(33);
|
||||
}
|
||||
const state_nested_class = $state({ value: new Person2() });
|
||||
|
||||
const nested_state = $state({ person: 'John', age: 33 });
|
||||
|
||||
setContext('foo', {
|
||||
nested_state,
|
||||
get class_nested_state() { return class_nested_state },
|
||||
get state_nested_class() { return state_nested_class }
|
||||
})
|
||||
</script>
|
||||
|
||||
<Sub {class_nested_state} {state_nested_class} {nested_state} />
|
||||
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { getContext } from "svelte";
|
||||
|
||||
const foo = getContext('foo')
|
||||
</script>
|
||||
|
||||
<button onclick={() => {
|
||||
foo.class_nested_state.value.age++;
|
||||
foo.state_nested_class.value.age++;
|
||||
foo.nested_state.age++;
|
||||
}}>mutate</button>
|
||||
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
const { settings } = $props();
|
||||
</script>
|
||||
|
||||
Child: {settings.showInRgb}
|
||||
@ -0,0 +1,22 @@
|
||||
import { flushSync } from '../../../../src/index-client';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
html: `<button>click true</button> Child: true`,
|
||||
|
||||
async test({ assert, target }) {
|
||||
const btn = target.querySelector('button');
|
||||
|
||||
flushSync(() => {
|
||||
btn?.click();
|
||||
});
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `<button>click false</button> Child: false`);
|
||||
|
||||
flushSync(() => {
|
||||
btn?.click();
|
||||
});
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `<button>click true</button> Child: true`);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,19 @@
|
||||
<script context="module">
|
||||
export const context = $state({
|
||||
settings: {
|
||||
showInRgb: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import Child from './Child.svelte';
|
||||
|
||||
const { settings } = context
|
||||
</script>
|
||||
|
||||
<button onclick={() => settings.showInRgb = !settings.showInRgb}>
|
||||
click {settings.showInRgb}
|
||||
</button>
|
||||
|
||||
<Child settings={settings} />
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue