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({
|
export default test({
|
||||||
test({ assert, component, window }) {
|
test({ assert, window }) {
|
||||||
document.dispatchEvent(new Event('DOMContentLoaded'));
|
document.dispatchEvent(new Event('DOMContentLoaded'));
|
||||||
assert.equal(window.document.querySelector('button')?.textContent, 'Hello world');
|
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