mirror of https://github.com/sveltejs/svelte
pull/16762/head
commit
380f45725b
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: send `$effect.pending` count to the correct boundary
|
@ -1,5 +0,0 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: properly catch top level await errors
|
@ -0,0 +1,10 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async: true,
|
||||
error: {
|
||||
code: 'const_tag_invalid_reference',
|
||||
message: 'The `{@const foo = ...}` declaration is not available in this snippet ',
|
||||
position: [376, 379]
|
||||
}
|
||||
});
|
@ -0,0 +1,32 @@
|
||||
<svelte:options runes />
|
||||
|
||||
<!-- ok -->
|
||||
<svelte:boundary>
|
||||
{@const foo = 'bar'}
|
||||
|
||||
{#snippet other()}
|
||||
{foo}
|
||||
{/snippet}
|
||||
|
||||
{foo}
|
||||
|
||||
<svelte:boundary>
|
||||
{#snippet failed()}
|
||||
{foo}
|
||||
{/snippet}
|
||||
</svelte:boundary>
|
||||
|
||||
{#snippet failed()}
|
||||
{@const foo = 'bar'}
|
||||
{foo}
|
||||
{/snippet}
|
||||
</svelte:boundary>
|
||||
|
||||
<!-- error -->
|
||||
<svelte:boundary>
|
||||
{@const foo = 'bar'}
|
||||
|
||||
{#snippet failed()}
|
||||
{foo}
|
||||
{/snippet}
|
||||
</svelte:boundary>
|
@ -0,0 +1,10 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async: true,
|
||||
error: {
|
||||
code: 'const_tag_invalid_reference',
|
||||
message: 'The `{@const foo = ...}` declaration is not available in this snippet ',
|
||||
position: [298, 301]
|
||||
}
|
||||
});
|
@ -0,0 +1,27 @@
|
||||
<svelte:options runes />
|
||||
|
||||
<!-- ok -->
|
||||
<Component>
|
||||
{@const foo = 'bar'}
|
||||
{foo}
|
||||
|
||||
<Component>
|
||||
{#snippet prop()}
|
||||
{foo}
|
||||
{/snippet}
|
||||
</Component>
|
||||
|
||||
{#snippet prop()}
|
||||
{@const foo = 'bar'}
|
||||
{foo}
|
||||
{/snippet}
|
||||
</Component>
|
||||
|
||||
<!-- error -->
|
||||
<Component>
|
||||
{@const foo = 'bar'}
|
||||
|
||||
{#snippet prop()}
|
||||
{foo}
|
||||
{/snippet}
|
||||
</Component>
|
@ -0,0 +1,15 @@
|
||||
<script>
|
||||
async function c(a) {
|
||||
await Promise.resolve()
|
||||
if (a) {
|
||||
throw new Error('error');
|
||||
} else {
|
||||
return 'ok';
|
||||
}
|
||||
}
|
||||
|
||||
let a = $state();
|
||||
let b = $derived(await c(a));
|
||||
</script>
|
||||
|
||||
<button onclick={() => a = 1}>{b}</button>
|
@ -0,0 +1,19 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
await tick();
|
||||
let [btn] = target.querySelectorAll('button');
|
||||
btn.click();
|
||||
await tick();
|
||||
|
||||
assert.htmlEqual(target.innerHTML, '<button>reset</button>');
|
||||
|
||||
[btn] = target.querySelectorAll('button');
|
||||
btn.click();
|
||||
await tick();
|
||||
|
||||
assert.htmlEqual(target.innerHTML, '<button>ok</button>');
|
||||
}
|
||||
});
|
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
import Test from './Test.svelte';
|
||||
</script>
|
||||
|
||||
<svelte:boundary>
|
||||
<Test />
|
||||
|
||||
{#snippet pending()}pending{/snippet}
|
||||
{#snippet failed(_, reset)}
|
||||
<button onclick={reset}>reset</button>
|
||||
{/snippet}
|
||||
</svelte:boundary>
|
@ -0,0 +1,41 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
const [increment, pop] = target.querySelectorAll('button');
|
||||
|
||||
increment.click();
|
||||
await tick();
|
||||
|
||||
pop.click();
|
||||
await tick();
|
||||
|
||||
pop.click();
|
||||
await tick();
|
||||
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>increment</button>
|
||||
<button>pop</button>
|
||||
<p>1</p>
|
||||
`
|
||||
);
|
||||
|
||||
increment.click();
|
||||
await tick();
|
||||
|
||||
pop.click();
|
||||
await tick();
|
||||
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>increment</button>
|
||||
<button>pop</button>
|
||||
<p>2</p>
|
||||
`
|
||||
);
|
||||
}
|
||||
});
|
@ -0,0 +1,35 @@
|
||||
|
||||
<script>
|
||||
let count = $state(0);
|
||||
|
||||
let deferreds = [];
|
||||
|
||||
class X {
|
||||
constructor(promise) {
|
||||
this.promise = promise;
|
||||
}
|
||||
|
||||
get then() {
|
||||
count;
|
||||
|
||||
return (resolve) => this.promise.then(() => count).then(resolve)
|
||||
}
|
||||
}
|
||||
|
||||
function push() {
|
||||
const deferred = Promise.withResolvers();
|
||||
deferreds.push(deferred);
|
||||
return new X(deferred.promise);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={() => count += 1}>increment</button>
|
||||
<button onclick={() => deferreds.pop()?.resolve(count)}>pop</button>
|
||||
|
||||
<svelte:boundary>
|
||||
<p>{await push()}</p>
|
||||
|
||||
{#snippet pending()}
|
||||
<p>loading...</p>
|
||||
{/snippet}
|
||||
</svelte:boundary>
|
@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from './main.svelte';
|
||||
|
||||
const bar = await new Promise((r) => resolve.push(() => r('bar')));
|
||||
</script>
|
||||
|
||||
<p>bar: {bar}</p>
|
@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from './main.svelte';
|
||||
import Bar from './Bar.svelte';
|
||||
|
||||
const foo = await new Promise((r) => resolve.push(() => r('foo')));
|
||||
</script>
|
||||
|
||||
<p>foo: {foo}</p>
|
||||
|
||||
<Bar/>
|
@ -0,0 +1,42 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
const [show, resolve] = target.querySelectorAll('button');
|
||||
|
||||
show.click();
|
||||
await tick();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>show</button>
|
||||
<button>resolve</button>
|
||||
<p>pending...</p>
|
||||
`
|
||||
);
|
||||
|
||||
resolve.click();
|
||||
await tick();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>show</button>
|
||||
<button>resolve</button>
|
||||
<p>pending...</p>
|
||||
`
|
||||
);
|
||||
|
||||
resolve.click();
|
||||
await tick();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>show</button>
|
||||
<button>resolve</button>
|
||||
<p>foo: foo</p>
|
||||
<p>bar: bar</p>
|
||||
`
|
||||
);
|
||||
}
|
||||
});
|
@ -0,0 +1,31 @@
|
||||
<script module>
|
||||
export let resolve = [];
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import Foo from './Foo.svelte';
|
||||
|
||||
let show = $state(false);
|
||||
</script>
|
||||
|
||||
<button onclick={() => show = true}>
|
||||
show
|
||||
</button>
|
||||
|
||||
<button onclick={() => resolve.shift()()}>
|
||||
resolve
|
||||
</button>
|
||||
|
||||
<svelte:boundary>
|
||||
{#if show}
|
||||
<Foo/>
|
||||
{/if}
|
||||
|
||||
{#if $effect.pending()}
|
||||
<p>pending...</p>
|
||||
{/if}
|
||||
|
||||
{#snippet pending()}
|
||||
<p>initializing...</p>
|
||||
{/snippet}
|
||||
</svelte:boundary>
|
@ -0,0 +1,9 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
await tick();
|
||||
assert.htmlEqual(target.innerHTML, 'value');
|
||||
}
|
||||
});
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
const value = await 'value';
|
||||
</script>
|
||||
|
||||
{#snippet valueSnippet()}
|
||||
{value}
|
||||
{/snippet}
|
||||
|
||||
{@render valueSnippet()}
|
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
import App from './app.svelte';
|
||||
</script>
|
||||
<svelte:boundary>
|
||||
{#snippet pending()}
|
||||
{/snippet}
|
||||
<App />
|
||||
</svelte:boundary>
|
@ -0,0 +1,9 @@
|
||||
import { tick } from 'svelte';
|
||||
import { ok, test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
await tick();
|
||||
assert.htmlEqual(target.innerHTML, '<p>foo bar</p>');
|
||||
}
|
||||
});
|
@ -0,0 +1,17 @@
|
||||
<script>
|
||||
function foo() {
|
||||
return 'foo';
|
||||
}
|
||||
|
||||
async function bar() {
|
||||
return Promise.resolve('bar');
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:boundary>
|
||||
<p>{foo()} {await bar()}</p>
|
||||
|
||||
{#snippet pending()}
|
||||
<p>pending</p>
|
||||
{/snippet}
|
||||
</svelte:boundary>
|
@ -0,0 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from './main.svelte';
|
||||
|
||||
const foo = $derived(await new Promise((r) => resolve.push(() => r('foo'))));
|
||||
const bar = $derived(await new Promise((r) => resolve.push(() => r('bar'))));
|
||||
</script>
|
||||
|
||||
<p>{foo} {bar}</p>
|
@ -0,0 +1,41 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
const [show, resolve] = target.querySelectorAll('button');
|
||||
|
||||
show.click();
|
||||
await tick();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>show</button>
|
||||
<button>resolve</button>
|
||||
<p>pending...</p>
|
||||
`
|
||||
);
|
||||
|
||||
resolve.click();
|
||||
await tick();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>show</button>
|
||||
<button>resolve</button>
|
||||
<p>pending...</p>
|
||||
`
|
||||
);
|
||||
|
||||
resolve.click();
|
||||
await tick();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>show</button>
|
||||
<button>resolve</button>
|
||||
<p>foo bar</p>
|
||||
`
|
||||
);
|
||||
}
|
||||
});
|
@ -0,0 +1,31 @@
|
||||
<script module>
|
||||
export let resolve = [];
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import Foo from './Foo.svelte';
|
||||
|
||||
let show = $state(false);
|
||||
</script>
|
||||
|
||||
<button onclick={() => show = true}>
|
||||
show
|
||||
</button>
|
||||
|
||||
<button onclick={() => resolve.shift()()}>
|
||||
resolve
|
||||
</button>
|
||||
|
||||
<svelte:boundary>
|
||||
{#if show}
|
||||
<Foo/>
|
||||
{/if}
|
||||
|
||||
{#if $effect.pending()}
|
||||
<p>pending...</p>
|
||||
{/if}
|
||||
|
||||
{#snippet pending()}
|
||||
<p>initializing...</p>
|
||||
{/snippet}
|
||||
</svelte:boundary>
|
@ -0,0 +1,28 @@
|
||||
import { flushSync } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
mode: ['client', 'hydrate'],
|
||||
|
||||
html: `<input><p>a</a>`,
|
||||
|
||||
async test({ assert, target }) {
|
||||
const [input] = target.querySelectorAll('input');
|
||||
|
||||
input.focus();
|
||||
input.value = 'ab';
|
||||
input.dispatchEvent(new InputEvent('input', { bubbles: true }));
|
||||
flushSync();
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `<input><p>ab</a>`);
|
||||
assert.equal(input.value, 'ab');
|
||||
|
||||
input.focus();
|
||||
input.value = 'abc';
|
||||
input.dispatchEvent(new InputEvent('input', { bubbles: true }));
|
||||
flushSync();
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `<input><p>abc</a>`);
|
||||
assert.equal(input.value, 'abc');
|
||||
}
|
||||
});
|
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
let array = $state([{ value: 'a' }]);
|
||||
</script>
|
||||
|
||||
{#each array as obj}
|
||||
<input bind:value={() => obj.value, (value) => array = [{ value }]} />
|
||||
<p>{obj.value}</p>
|
||||
{/each}
|
@ -0,0 +1,30 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
mode: ['client', 'hydrate'],
|
||||
|
||||
async test({ assert, target }) {
|
||||
const [input] = target.querySelectorAll('input');
|
||||
|
||||
input.focus();
|
||||
input.value = 'Ab';
|
||||
input.dispatchEvent(new InputEvent('input', { bubbles: true }));
|
||||
|
||||
await tick();
|
||||
await tick();
|
||||
|
||||
assert.equal(input.value, 'AB');
|
||||
assert.htmlEqual(target.innerHTML, `<input /><p>AB</p>`);
|
||||
|
||||
input.focus();
|
||||
input.value = 'ABc';
|
||||
input.dispatchEvent(new InputEvent('input', { bubbles: true }));
|
||||
|
||||
await tick();
|
||||
await tick();
|
||||
|
||||
assert.equal(input.value, 'ABC');
|
||||
assert.htmlEqual(target.innerHTML, `<input /><p>ABC</p>`);
|
||||
}
|
||||
});
|
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
let text = $state('A');
|
||||
</script>
|
||||
|
||||
<input bind:value={() => text, (v) => text = v.toUpperCase()} />
|
||||
<p>{text}</p>
|
@ -1,3 +1,3 @@
|
||||
<script>
|
||||
throw new Error();
|
||||
</script>
|
||||
</script>
|
@ -0,0 +1,18 @@
|
||||
import { flushSync } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
skip_async: true,
|
||||
html: '<button></button><p>2</p>',
|
||||
mode: ['client'],
|
||||
test({ target, assert }) {
|
||||
const btn = target.querySelector('button');
|
||||
const p = target.querySelector('p');
|
||||
|
||||
flushSync(() => {
|
||||
btn?.click();
|
||||
});
|
||||
|
||||
assert.equal(p?.innerHTML, '4');
|
||||
}
|
||||
});
|
@ -0,0 +1,14 @@
|
||||
<script>
|
||||
import FlakyComponent from "./FlakyComponent.svelte";
|
||||
let test=$state(1);
|
||||
</script>
|
||||
|
||||
<button onclick={()=>test++}></button>
|
||||
|
||||
<svelte:boundary>
|
||||
{@const double = test * 2}
|
||||
{#snippet failed()}
|
||||
<p>{double}</p>
|
||||
{/snippet}
|
||||
<FlakyComponent />
|
||||
</svelte:boundary>
|
@ -1,14 +1,10 @@
|
||||
<script>
|
||||
import FlakyComponent from "./FlakyComponent.svelte";
|
||||
let test=$state(1);
|
||||
let count = $state(1);
|
||||
</script>
|
||||
|
||||
<button onclick={()=>test++}></button>
|
||||
<button onclick={()=>count++}>increment</button>
|
||||
|
||||
<svelte:boundary>
|
||||
{@const double = test * 2}
|
||||
{#snippet failed()}
|
||||
<p>{double}</p>
|
||||
{/snippet}
|
||||
<FlakyComponent />
|
||||
</svelte:boundary>
|
||||
{@const double = count * 2}
|
||||
<p>{double}</p>
|
||||
</svelte:boundary>
|
||||
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
let { text } = $props();
|
||||
|
||||
$effect(() => console.log(text));
|
||||
</script>
|
||||
|
||||
{text}
|
@ -0,0 +1,12 @@
|
||||
import { async_mode } from '../../../helpers';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
// In legacy mode this succeeds and logs 'hello'
|
||||
// In async mode this throws an error because flushSync is called inside an effect
|
||||
async test({ assert, target, logs }) {
|
||||
assert.htmlEqual(target.innerHTML, `<button>show</button> <div>hello</div>`);
|
||||
assert.deepEqual(logs, ['hello']);
|
||||
},
|
||||
runtime_error: async_mode ? 'flush_sync_in_effect' : undefined
|
||||
});
|
@ -0,0 +1,13 @@
|
||||
<script>
|
||||
import { flushSync, mount } from 'svelte'
|
||||
import Child from './Child.svelte';
|
||||
|
||||
let show = $state(false);
|
||||
</script>
|
||||
|
||||
<button onclick={() => show = true}>show</button>
|
||||
|
||||
<div {@attach (target) => {
|
||||
mount(Child, { target, props: { text: 'hello' } });
|
||||
flushSync();
|
||||
}}></div>
|
Loading…
Reference in new issue