mirror of https://github.com/sveltejs/svelte
commit
3d34b0c444
@ -1,6 +1,29 @@
|
||||
/** @import {} from 'trusted-types' */
|
||||
|
||||
import { create_element } from './operations.js';
|
||||
|
||||
const policy = /* @__PURE__ */ globalThis?.window?.trustedTypes?.createPolicy(
|
||||
'svelte-trusted-html',
|
||||
{
|
||||
/** @param {string} html */
|
||||
createHTML: (html) => {
|
||||
return html;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/** @param {string} html */
|
||||
export function create_fragment_from_html(html) {
|
||||
var elem = document.createElement('template');
|
||||
elem.innerHTML = html.replaceAll('<!>', '<!---->'); // XHTML compliance
|
||||
function create_trusted_html(html) {
|
||||
return /** @type {string} */ (policy?.createHTML(html) ?? html);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} html
|
||||
* @param {boolean} trusted
|
||||
*/
|
||||
export function create_fragment_from_html(html, trusted = false) {
|
||||
var elem = create_element('template');
|
||||
html = html.replaceAll('<!>', '<!---->'); // XHTML compliance
|
||||
elem.innerHTML = trusted ? create_trusted_html(html) : html;
|
||||
return elem.content;
|
||||
}
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
<svelte:head><title>Page Title</title></svelte:head><div>no space</div>
|
||||
<Component /><Component />
|
||||
<Component><span>child</span></Component><div>after component</div>
|
||||
<svelte:boundary><div>boundary content</div></svelte:boundary><div>after boundary</div>
|
||||
<!--comment--><div>after comment</div>
|
||||
<div>before comment</div><!--comment-->
|
||||
{#each items as item}<div>{item}</div>{/each}<div>after each</div>
|
||||
{@render children()}<div>after render</div>
|
||||
<div>before render</div>{@render children()}
|
||||
|
||||
<Component /> <div>with spaces</div>
|
||||
<Component><span>child</span></Component> <div>spaces after component</div>
|
||||
<!--comment--> <div>spaces after comment</div>
|
||||
{@render children()} <div>spaces after render</div>
|
||||
|
||||
<Component />
|
||||
<div>newline between</div>
|
||||
<!--comment-->
|
||||
<div>newline after comment</div>
|
||||
{#each items as item}
|
||||
<div>{item}</div>
|
||||
{/each}
|
||||
<div>newline after each</div>
|
||||
{@render children()}
|
||||
<div>after render</div>
|
||||
@ -0,0 +1,42 @@
|
||||
<svelte:head><title>Page Title</title></svelte:head>
|
||||
<div>no space</div>
|
||||
<Component />
|
||||
<Component />
|
||||
<Component><span>child</span></Component>
|
||||
<div>after component</div>
|
||||
<svelte:boundary><div>boundary content</div></svelte:boundary>
|
||||
<div>after boundary</div>
|
||||
<!--comment-->
|
||||
<div>after comment</div>
|
||||
<div>before comment</div>
|
||||
<!--comment-->
|
||||
|
||||
{#each items as item}
|
||||
<div>{item}</div>
|
||||
{/each}
|
||||
|
||||
<div>after each</div>
|
||||
{@render children()}
|
||||
<div>after render</div>
|
||||
<div>before render</div>
|
||||
{@render children()}
|
||||
<Component />
|
||||
<div>with spaces</div>
|
||||
<Component><span>child</span></Component>
|
||||
<div>spaces after component</div>
|
||||
<!--comment-->
|
||||
<div>spaces after comment</div>
|
||||
{@render children()}
|
||||
<div>spaces after render</div>
|
||||
<Component />
|
||||
<div>newline between</div>
|
||||
<!--comment-->
|
||||
<div>newline after comment</div>
|
||||
|
||||
{#each items as item}
|
||||
<div>{item}</div>
|
||||
{/each}
|
||||
|
||||
<div>newline after each</div>
|
||||
{@render children()}
|
||||
<div>after render</div>
|
||||
@ -0,0 +1,37 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
ssrHtml: `
|
||||
<select>
|
||||
<option selected="" value="">Select</option>
|
||||
<option value="us">US</option>
|
||||
<option value="uk">UK</option>
|
||||
</select>
|
||||
`,
|
||||
|
||||
async test({ assert, target, window, variant }) {
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<select>
|
||||
<option${variant === 'hydrate' ? ' selected=""' : ''} value="">Select</option>
|
||||
<option value="us">US</option>
|
||||
<option value="uk">UK</option>
|
||||
</select>
|
||||
`
|
||||
);
|
||||
|
||||
const [select] = target.querySelectorAll('select');
|
||||
const options = target.querySelectorAll('option');
|
||||
|
||||
assert.equal(select.value, '');
|
||||
|
||||
const change = new window.Event('change');
|
||||
|
||||
// Select "UK"
|
||||
options[2].selected = true;
|
||||
await select.dispatchEvent(change);
|
||||
|
||||
assert.equal(select.value, 'uk');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,21 @@
|
||||
<script>
|
||||
const default_details = {
|
||||
country: '',
|
||||
}
|
||||
|
||||
$: data = {
|
||||
locked: false,
|
||||
details: null,
|
||||
}
|
||||
|
||||
$: details = data.details ?? default_details
|
||||
</script>
|
||||
|
||||
<select
|
||||
bind:value={details.country}
|
||||
disabled={data.locked}
|
||||
>
|
||||
<option value="">Select</option>
|
||||
<option value="us">US</option>
|
||||
<option value="uk">UK</option>
|
||||
</select>
|
||||
@ -0,0 +1,14 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
mode: ['async-server', 'client', 'hydrate'],
|
||||
ssrHtml: 'true true true true true',
|
||||
|
||||
async test({ assert, target }) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
await tick();
|
||||
|
||||
assert.htmlEqual(target.innerHTML, 'true true true true true');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,41 @@
|
||||
<script>
|
||||
let checked = $derived(await new Promise((r) => setTimeout(() => r(true)), 10));
|
||||
|
||||
const checkedFactory = () => {
|
||||
return () => checked;
|
||||
}
|
||||
|
||||
function indirectCheckedFactory() {
|
||||
return checkedFactory();
|
||||
}
|
||||
|
||||
function callFactory(factory) {
|
||||
return factory();
|
||||
}
|
||||
|
||||
function indirectCallFactory() {
|
||||
return callFactory(indirectCheckedFactory);
|
||||
}
|
||||
|
||||
function indirectChecked2() {
|
||||
const indirect = () => checkedFactory()();
|
||||
return indirect;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- force into separate effects -->
|
||||
{#if true}
|
||||
{checkedFactory()()}
|
||||
{/if}
|
||||
{#if true}
|
||||
{indirectCheckedFactory()()}
|
||||
{/if}
|
||||
{#if true}
|
||||
{callFactory(checkedFactory)()}
|
||||
{/if}
|
||||
{#if true}
|
||||
{indirectCallFactory()()}
|
||||
{/if}
|
||||
{#if true}
|
||||
{indirectChecked2()()}
|
||||
{/if}
|
||||
@ -0,0 +1,12 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
mode: ['async-server', 'hydrate', 'client'],
|
||||
ssrHtml: `bar blocking`,
|
||||
|
||||
async test({ assert, target }) {
|
||||
await tick();
|
||||
assert.htmlEqual(target.innerHTML, 'bar blocking');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,21 @@
|
||||
<script>
|
||||
let foo = $state(false);
|
||||
let blocking = $derived(await foo);
|
||||
let bar = Promise.resolve(true);
|
||||
</script>
|
||||
|
||||
{#if foo}
|
||||
foo
|
||||
{:else if await bar}
|
||||
bar
|
||||
{:else}
|
||||
else
|
||||
{/if}
|
||||
|
||||
{#if foo}
|
||||
foo
|
||||
{:else if !blocking}
|
||||
blocking
|
||||
{:else}
|
||||
else
|
||||
{/if}
|
||||
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
let { x } = $props();
|
||||
console.log(x);
|
||||
$effect(() => console.log('$effect: '+ x))
|
||||
</script>
|
||||
|
||||
{x}
|
||||
@ -0,0 +1,47 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
skip: true, // this fails on main, too; skip for now
|
||||
async test({ assert, target, logs }) {
|
||||
const [x, y, resolve] = target.querySelectorAll('button');
|
||||
|
||||
x.click();
|
||||
await tick();
|
||||
assert.deepEqual(logs, ['universe']);
|
||||
|
||||
y.click();
|
||||
await tick();
|
||||
assert.deepEqual(logs, ['universe', 'world', '$effect: world']);
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>x</button>
|
||||
<button>y++</button>
|
||||
<button>resolve</button>
|
||||
world
|
||||
`
|
||||
);
|
||||
|
||||
resolve.click();
|
||||
await tick();
|
||||
assert.deepEqual(logs, [
|
||||
'universe',
|
||||
'world',
|
||||
'$effect: world',
|
||||
'$effect: universe',
|
||||
'$effect: universe'
|
||||
]);
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>x</button>
|
||||
<button>y++</button>
|
||||
<button>resolve</button>
|
||||
universe
|
||||
universe
|
||||
universe
|
||||
`
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,28 @@
|
||||
<script>
|
||||
import Child from './Child.svelte';
|
||||
|
||||
let x = $state('world');
|
||||
let y = $state(0);
|
||||
let deferred = [];
|
||||
|
||||
function delay(s) {
|
||||
const d = Promise.withResolvers();
|
||||
deferred.push(() => d.resolve(s))
|
||||
return d.promise;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={() => x = 'universe'}>x</button>
|
||||
|
||||
<button onclick={() => y++}>y++</button>
|
||||
|
||||
<button onclick={() => deferred.shift()()}>resolve</button>
|
||||
|
||||
{#if x === 'universe'}
|
||||
{await delay(x)}
|
||||
<Child {x} />
|
||||
{/if}
|
||||
|
||||
{#if y > 0}
|
||||
<Child {x} />
|
||||
{/if}
|
||||
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
$effect(() => {
|
||||
throw new Error('boom');
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,10 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
// allow effects to run / microtasks to flush
|
||||
await Promise.resolve();
|
||||
|
||||
assert.htmlEqual(target.innerHTML, '<p>caught: boom</p>');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import Test from './Test.svelte';
|
||||
</script>
|
||||
|
||||
<svelte:boundary>
|
||||
<Test />
|
||||
|
||||
{#snippet failed(e)}
|
||||
<p>caught: {e.message}</p>
|
||||
{/snippet}
|
||||
</svelte:boundary>
|
||||
@ -0,0 +1,42 @@
|
||||
import { flushSync } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
test({ assert, target, logs }) {
|
||||
const [toggle, run] = target.querySelectorAll('button');
|
||||
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
'<button>toggle</button><button>run</button><p>hello: 0</p>'
|
||||
);
|
||||
|
||||
flushSync(() => run.click());
|
||||
assert.deepEqual(logs, []);
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
'<button>toggle</button><button>run</button><p>hello: 1</p>'
|
||||
);
|
||||
|
||||
flushSync(() => toggle.click());
|
||||
assert.deepEqual(logs, ['aborted']);
|
||||
assert.htmlEqual(target.innerHTML, '<button>toggle</button><button>run</button>');
|
||||
|
||||
flushSync(() => run.click());
|
||||
flushSync(() => run.click());
|
||||
flushSync(() => run.click());
|
||||
|
||||
flushSync(() => toggle.click());
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
'<button>toggle</button><button>run</button><p>hello: 1</p>'
|
||||
);
|
||||
|
||||
flushSync(() => run.click());
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
'<button>toggle</button><button>run</button><p>hello: 2</p>'
|
||||
);
|
||||
|
||||
assert.deepEqual(logs, ['aborted']);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,53 @@
|
||||
<script>
|
||||
import { getAbortSignal } from 'svelte';
|
||||
|
||||
const callbacks = new Map();
|
||||
|
||||
// similar semantics to setInterval, but simpler to test
|
||||
function add(fn) {
|
||||
const id = crypto.randomUUID();
|
||||
callbacks.set(id, fn);
|
||||
return id;
|
||||
}
|
||||
|
||||
function remove(id) {
|
||||
callbacks.delete(id);
|
||||
}
|
||||
|
||||
function run() {
|
||||
for (const fn of callbacks.values()) {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
|
||||
class Timer {
|
||||
constructor(text) {
|
||||
this.elapsed = $state(0);
|
||||
this.text = $derived(text + ': ' + this.elapsed);
|
||||
|
||||
$effect(() => {
|
||||
const id = add(() => {
|
||||
this.elapsed += 1;
|
||||
});
|
||||
|
||||
getAbortSignal().onabort = () => {
|
||||
console.log('aborted');
|
||||
};
|
||||
|
||||
return () => remove(id);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let timer = $derived(new Timer('hello'));
|
||||
|
||||
let visible = $state(true);
|
||||
</script>
|
||||
|
||||
<button onclick={() => visible = !visible}>toggle</button>
|
||||
<button onclick={run}>run</button>
|
||||
|
||||
{#if visible}
|
||||
<p>{timer.text}</p>
|
||||
{/if}
|
||||
@ -0,0 +1,72 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test, ok } from '../../test';
|
||||
|
||||
export default test({
|
||||
html: `
|
||||
<input type="text"/>
|
||||
<input type="text"/>
|
||||
<p>x / y</p>
|
||||
|
||||
<button>change to text</button>
|
||||
<button>change to number</button>
|
||||
<button>change to range</button>
|
||||
`,
|
||||
ssrHtml: `
|
||||
<input type="text" value="x"/>
|
||||
<input type="text" value="y"/>
|
||||
<p>x / y</p>
|
||||
|
||||
<button>change to text</button>
|
||||
<button>change to number</button>
|
||||
<button>change to range</button>
|
||||
`,
|
||||
async test({ assert, target }) {
|
||||
const [in1, in2] = target.querySelectorAll('input');
|
||||
const [btn1, btn2, btn3] = target.querySelectorAll('button');
|
||||
const p = target.querySelector('p');
|
||||
ok(p);
|
||||
|
||||
in1.value = '0';
|
||||
in2.value = '1';
|
||||
in1.dispatchEvent(new window.Event('input', { bubbles: true }));
|
||||
in2.dispatchEvent(new window.Event('input', { bubbles: true }));
|
||||
await tick();
|
||||
btn2?.click();
|
||||
await tick();
|
||||
assert.htmlEqual(p.innerHTML, '0 / 1');
|
||||
|
||||
in1.stepUp();
|
||||
in1.dispatchEvent(new window.Event('input', { bubbles: true }));
|
||||
in2.stepUp();
|
||||
in2.dispatchEvent(new window.Event('input', { bubbles: true }));
|
||||
await tick();
|
||||
assert.htmlEqual(p.innerHTML, '1 / 2');
|
||||
|
||||
btn1?.click();
|
||||
await tick();
|
||||
try {
|
||||
in1.stepUp();
|
||||
assert.fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
btn3?.click();
|
||||
await tick();
|
||||
in1.stepUp();
|
||||
in1.dispatchEvent(new window.Event('input', { bubbles: true }));
|
||||
in2.stepUp();
|
||||
in2.dispatchEvent(new window.Event('input', { bubbles: true }));
|
||||
await tick();
|
||||
assert.htmlEqual(p.innerHTML, '2 / 3');
|
||||
|
||||
btn1?.click();
|
||||
await tick();
|
||||
in1.value = 'a';
|
||||
in2.value = 'b';
|
||||
in1.dispatchEvent(new window.Event('input', { bubbles: true }));
|
||||
in2.dispatchEvent(new window.Event('input', { bubbles: true }));
|
||||
await tick();
|
||||
assert.htmlEqual(p.innerHTML, 'a / b');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,14 @@
|
||||
<script>
|
||||
let dynamic = $state('x');
|
||||
let spread = $state('y');
|
||||
let inputType = $state('text');
|
||||
let props = $derived({type: inputType});
|
||||
</script>
|
||||
|
||||
<input bind:value={dynamic} type={inputType}>
|
||||
<input bind:value={spread} {...props}>
|
||||
<p>{dynamic} / {spread}</p>
|
||||
|
||||
<button onclick={() => inputType = 'text'}>change to text</button>
|
||||
<button onclick={() => inputType = 'number'}>change to number</button>
|
||||
<button onclick={() => inputType = 'range'}>change to range</button>
|
||||
@ -0,0 +1,263 @@
|
||||
import { test } from '../../test';
|
||||
import { flushSync } from 'svelte';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
/**
|
||||
* @param {NodeListOf<any>} inputs
|
||||
* @param {string} field
|
||||
* @param {any | any[]} value
|
||||
*/
|
||||
function check_inputs(inputs, field, value) {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
assert.equal(inputs[i][field], Array.isArray(value) ? value[i] : value, `field ${i}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} input
|
||||
* @param {string} field
|
||||
* @param {any} value
|
||||
*/
|
||||
function set_input(input, field, value) {
|
||||
input[field] = value;
|
||||
input.dispatchEvent(
|
||||
new Event(typeof value === 'boolean' ? 'change' : 'input', { bubbles: true })
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLOptionElement} option
|
||||
*/
|
||||
function select_option(option) {
|
||||
option.selected = true;
|
||||
option.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
|
||||
const after_reset = [];
|
||||
|
||||
const reset = /** @type {HTMLInputElement} */ (target.querySelector('input[type=reset]'));
|
||||
const [test1, test2, test3, test4, test5, test6, test7, test14] =
|
||||
target.querySelectorAll('div');
|
||||
const [test8, test9, test10, test11] = target.querySelectorAll('select');
|
||||
const [
|
||||
test1_span,
|
||||
test2_span,
|
||||
test3_span,
|
||||
test4_span,
|
||||
test5_span,
|
||||
test6_span,
|
||||
test7_span,
|
||||
test8_span,
|
||||
test9_span,
|
||||
test10_span,
|
||||
test11_span
|
||||
] = target.querySelectorAll('span');
|
||||
|
||||
{
|
||||
/** @type {NodeListOf<HTMLInputElement | HTMLTextAreaElement>} */
|
||||
const inputs = test1.querySelectorAll('input, textarea');
|
||||
check_inputs(inputs, 'value', 'x');
|
||||
assert.htmlEqual(test1_span.innerHTML, 'x x x x');
|
||||
|
||||
for (const input of inputs) {
|
||||
set_input(input, 'value', 'foo');
|
||||
}
|
||||
flushSync();
|
||||
check_inputs(inputs, 'value', 'foo');
|
||||
assert.htmlEqual(test1_span.innerHTML, 'foo foo foo foo');
|
||||
|
||||
after_reset.push(() => {
|
||||
check_inputs(inputs, 'value', 'x');
|
||||
assert.htmlEqual(test1_span.innerHTML, 'x x x x');
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
/** @type {NodeListOf<HTMLInputElement | HTMLTextAreaElement>} */
|
||||
const inputs = test2.querySelectorAll('input, textarea');
|
||||
check_inputs(inputs, 'value', 'x');
|
||||
assert.htmlEqual(test2_span.innerHTML, 'x x x x');
|
||||
|
||||
for (const input of inputs) {
|
||||
set_input(input, 'value', 'foo');
|
||||
}
|
||||
flushSync();
|
||||
check_inputs(inputs, 'value', 'foo');
|
||||
assert.htmlEqual(test2_span.innerHTML, 'foo foo foo foo');
|
||||
|
||||
after_reset.push(() => {
|
||||
check_inputs(inputs, 'value', 'x');
|
||||
assert.htmlEqual(test2_span.innerHTML, 'x x x x');
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
/** @type {NodeListOf<HTMLInputElement | HTMLTextAreaElement>} */
|
||||
const inputs = test3.querySelectorAll('input, textarea');
|
||||
check_inputs(inputs, 'value', 'y');
|
||||
assert.htmlEqual(test3_span.innerHTML, 'y y y y');
|
||||
|
||||
for (const input of inputs) {
|
||||
set_input(input, 'value', 'foo');
|
||||
}
|
||||
flushSync();
|
||||
check_inputs(inputs, 'value', 'foo');
|
||||
assert.htmlEqual(test3_span.innerHTML, 'foo foo foo foo');
|
||||
|
||||
after_reset.push(() => {
|
||||
check_inputs(inputs, 'value', 'x');
|
||||
assert.htmlEqual(test3_span.innerHTML, 'x x x x');
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
/** @type {NodeListOf<HTMLInputElement>} */
|
||||
const inputs = test4.querySelectorAll('input');
|
||||
check_inputs(inputs, 'checked', true);
|
||||
assert.htmlEqual(test4_span.innerHTML, 'true true');
|
||||
|
||||
for (const input of inputs) {
|
||||
set_input(input, 'checked', false);
|
||||
}
|
||||
flushSync();
|
||||
check_inputs(inputs, 'checked', false);
|
||||
assert.htmlEqual(test4_span.innerHTML, 'false false');
|
||||
|
||||
after_reset.push(() => {
|
||||
check_inputs(inputs, 'checked', true);
|
||||
assert.htmlEqual(test4_span.innerHTML, 'true true');
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
/** @type {NodeListOf<HTMLInputElement>} */
|
||||
const inputs = test5.querySelectorAll('input');
|
||||
check_inputs(inputs, 'checked', true);
|
||||
assert.htmlEqual(test5_span.innerHTML, 'true true');
|
||||
|
||||
for (const input of inputs) {
|
||||
set_input(input, 'checked', false);
|
||||
}
|
||||
flushSync();
|
||||
check_inputs(inputs, 'checked', false);
|
||||
assert.htmlEqual(test5_span.innerHTML, 'false false');
|
||||
|
||||
after_reset.push(() => {
|
||||
check_inputs(inputs, 'checked', true);
|
||||
assert.htmlEqual(test5_span.innerHTML, 'true true');
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
/** @type {NodeListOf<HTMLInputElement>} */
|
||||
const inputs = test6.querySelectorAll('input');
|
||||
check_inputs(inputs, 'checked', false);
|
||||
assert.htmlEqual(test6_span.innerHTML, 'false false');
|
||||
|
||||
after_reset.push(() => {
|
||||
check_inputs(inputs, 'checked', true);
|
||||
assert.htmlEqual(test6_span.innerHTML, 'true true');
|
||||
});
|
||||
}
|
||||
{
|
||||
/** @type {NodeListOf<HTMLInputElement>} */
|
||||
const inputs = test7.querySelectorAll('input');
|
||||
check_inputs(inputs, 'checked', true);
|
||||
assert.htmlEqual(test7_span.innerHTML, 'true');
|
||||
|
||||
after_reset.push(() => {
|
||||
check_inputs(inputs, 'checked', false);
|
||||
assert.htmlEqual(test7_span.innerHTML, 'false');
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
/** @type {NodeListOf<HTMLOptionElement>} */
|
||||
const options = test8.querySelectorAll('option');
|
||||
check_inputs(options, 'selected', [false, true, false]);
|
||||
assert.htmlEqual(test8_span.innerHTML, 'b');
|
||||
|
||||
select_option(options[2]);
|
||||
flushSync();
|
||||
check_inputs(options, 'selected', [false, false, true]);
|
||||
assert.htmlEqual(test8_span.innerHTML, 'c');
|
||||
|
||||
after_reset.push(() => {
|
||||
check_inputs(options, 'selected', [false, true, false]);
|
||||
assert.htmlEqual(test8_span.innerHTML, 'b');
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
/** @type {NodeListOf<HTMLOptionElement>} */
|
||||
const options = test9.querySelectorAll('option');
|
||||
check_inputs(options, 'selected', [false, true, false]);
|
||||
assert.htmlEqual(test9_span.innerHTML, 'b');
|
||||
|
||||
select_option(options[2]);
|
||||
flushSync();
|
||||
check_inputs(options, 'selected', [false, false, true]);
|
||||
assert.htmlEqual(test9_span.innerHTML, 'c');
|
||||
|
||||
after_reset.push(() => {
|
||||
check_inputs(options, 'selected', [false, true, false]);
|
||||
assert.htmlEqual(test9_span.innerHTML, 'b');
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
/** @type {NodeListOf<HTMLOptionElement>} */
|
||||
const options = test10.querySelectorAll('option');
|
||||
check_inputs(options, 'selected', [false, false, true]);
|
||||
assert.htmlEqual(test10_span.innerHTML, 'c');
|
||||
|
||||
select_option(options[0]);
|
||||
flushSync();
|
||||
check_inputs(options, 'selected', [true, false, false]);
|
||||
assert.htmlEqual(test10_span.innerHTML, 'a');
|
||||
|
||||
after_reset.push(() => {
|
||||
check_inputs(options, 'selected', [false, true, false]);
|
||||
assert.htmlEqual(test10_span.innerHTML, 'b');
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
/** @type {NodeListOf<HTMLOptionElement>} */
|
||||
const options = test11.querySelectorAll('option');
|
||||
check_inputs(options, 'selected', [false, false, true]);
|
||||
assert.htmlEqual(test11_span.innerHTML, 'c');
|
||||
|
||||
select_option(options[0]);
|
||||
flushSync();
|
||||
check_inputs(options, 'selected', [true, false, false]);
|
||||
assert.htmlEqual(test11_span.innerHTML, 'a');
|
||||
|
||||
after_reset.push(() => {
|
||||
check_inputs(options, 'selected', [false, true, false]);
|
||||
assert.htmlEqual(test11_span.innerHTML, 'b');
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
/** @type {NodeListOf<HTMLInputElement | HTMLTextAreaElement>} */
|
||||
const inputs = test14.querySelectorAll('input, textarea');
|
||||
assert.equal(inputs[0].value, 'x');
|
||||
assert.equal(/** @type {HTMLInputElement} */ (inputs[1]).checked, true);
|
||||
// this is still missing...i have no idea how to fix this lol
|
||||
// assert.equal(inputs[2].value, 'x');
|
||||
|
||||
after_reset.push(() => {
|
||||
assert.equal(inputs[0].value, 'y');
|
||||
assert.equal(/** @type {HTMLInputElement} */ (inputs[1]).checked, false);
|
||||
assert.equal(inputs[2].value, 'y');
|
||||
});
|
||||
}
|
||||
|
||||
reset.click();
|
||||
await Promise.resolve();
|
||||
flushSync();
|
||||
after_reset.forEach((fn) => fn());
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,199 @@
|
||||
<script>
|
||||
let spread = {};
|
||||
|
||||
let value1 = $state();
|
||||
let value2 = $state();
|
||||
let value3 = $state();
|
||||
let value4 = $state();
|
||||
let value5 = $state();
|
||||
let value6 = $state();
|
||||
let value7 = $state();
|
||||
let value8 = $state();
|
||||
let value9 = $state(null);
|
||||
let value10 = $state(null);
|
||||
let value11 = $state(null);
|
||||
let value12 = $state(null);
|
||||
let value13 = $state(null);
|
||||
let value14 = $state(null);
|
||||
let value15 = $state(null);
|
||||
let value16 = $state(null);
|
||||
let value17 = $state('y');
|
||||
let value18 = $state('y');
|
||||
let value19 = $state('y');
|
||||
let value20 = $state('y');
|
||||
let value21 = $state('y');
|
||||
let value22 = $state('y');
|
||||
let value23 = $state('y');
|
||||
let value24 = $state('y');
|
||||
|
||||
let checked1 = $state();
|
||||
let checked2 = $state();
|
||||
let checked3 = $state();
|
||||
let checked4 = $state();
|
||||
let checked5 = $state(null);
|
||||
let checked6 = $state(null);
|
||||
let checked7 = $state(null);
|
||||
let checked8 = $state(null);
|
||||
let checked9 = $state(false);
|
||||
let checked10 = $state(false);
|
||||
let checked11 = $state(false);
|
||||
let checked12 = $state(false);
|
||||
let checked13 = $state(true);
|
||||
let checked14 = $state(true);
|
||||
|
||||
|
||||
let selected1 = $state();
|
||||
let selected2 = $state();
|
||||
let selected3 = $state('c');
|
||||
let selected4 = $state('c');
|
||||
let selected5 = $state(['c']);
|
||||
let selected6 = $state(['c']);
|
||||
|
||||
let defaultValue = $state('x');
|
||||
let defaultChecked = $state(true);
|
||||
</script>
|
||||
|
||||
<form>
|
||||
<p>Input/Textarea value</p>
|
||||
<!-- defaultValue=x, value=undefined -->
|
||||
<div class="test-1">
|
||||
<input {defaultValue} bind:value={value1} {...spread} />
|
||||
<input {defaultValue} value={value2} {...spread} />
|
||||
<input defaultValue="x" bind:value={value3} {...spread} />
|
||||
<input defaultValue="x" value={value4} {...spread} />
|
||||
<textarea {defaultValue} value={value5} {...spread}></textarea>
|
||||
<textarea {defaultValue} bind:value={value6} {...spread}></textarea>
|
||||
<textarea defaultValue="x" value={value7} {...spread}></textarea>
|
||||
<textarea defaultValue="x" bind:value={value8} {...spread}></textarea>
|
||||
</div>
|
||||
|
||||
<!-- defaultValue=x, value=null -->
|
||||
<div class="test-2">
|
||||
<input {defaultValue} bind:value={value9} {...spread} />
|
||||
<input {defaultValue} value={value10} {...spread} />
|
||||
<input defaultValue="x" value={value11} {...spread} />
|
||||
<input defaultValue="x" bind:value={value12} {...spread} />
|
||||
<textarea {defaultValue} value={value13} {...spread}></textarea>
|
||||
<textarea {defaultValue} bind:value={value14} {...spread}></textarea>
|
||||
<textarea defaultValue="x" value={value15} {...spread}></textarea>
|
||||
<textarea defaultValue="x" bind:value={value16} {...spread}></textarea>
|
||||
</div>
|
||||
|
||||
<!-- defaultValue=x, value=y -->
|
||||
<div class="test-3">
|
||||
<input {defaultValue} bind:value={value17} {...spread} />
|
||||
<input {defaultValue} value={value18} {...spread} />
|
||||
<input defaultValue="x" value={value19} {...spread} />
|
||||
<input defaultValue="x" bind:value={value20} {...spread} />
|
||||
<textarea {defaultValue} value={value21} {...spread}></textarea>
|
||||
<textarea {defaultValue} bind:value={value22} {...spread}></textarea>
|
||||
<textarea defaultValue="x" value={value23} {...spread}></textarea>
|
||||
<textarea defaultValue="x" bind:value={value24} {...spread}></textarea>
|
||||
</div>
|
||||
|
||||
<p>Input checked</p>
|
||||
<!-- defaultChecked=true, checked=undefined -->
|
||||
<div class="test-4">
|
||||
<input type="checkbox" {defaultChecked} checked={checked1} {...spread} />
|
||||
<input type="checkbox" {defaultChecked} bind:checked={checked2} {...spread} />
|
||||
<input type="checkbox" defaultChecked checked={checked3} {...spread} />
|
||||
<input type="checkbox" defaultChecked bind:checked={checked4} {...spread} />
|
||||
</div>
|
||||
|
||||
<!-- defaultChecked=true, checked=null -->
|
||||
<div class="test-5">
|
||||
<input type="checkbox" {defaultChecked} checked={checked5} {...spread} />
|
||||
<input type="checkbox" {defaultChecked} bind:checked={checked6} {...spread} />
|
||||
<input type="checkbox" defaultChecked checked={checked7} {...spread} />
|
||||
<input type="checkbox" defaultChecked bind:checked={checked8} {...spread} />
|
||||
</div>
|
||||
|
||||
<!-- defaultChecked=true, checked=false -->
|
||||
<div class="test-6">
|
||||
<input type="checkbox" {defaultChecked} checked={checked9} {...spread} />
|
||||
<input type="checkbox" {defaultChecked} bind:checked={checked10} {...spread} />
|
||||
<input type="checkbox" defaultChecked checked={checked11} {...spread} />
|
||||
<input type="checkbox" defaultChecked bind:checked={checked12} {...spread} />
|
||||
</div>
|
||||
|
||||
<!-- defaultChecked=false, checked=true -->
|
||||
<div class="test-7">
|
||||
<input type="checkbox" defaultChecked={false} checked={checked13} {...spread} />
|
||||
<input type="checkbox" defaultChecked={false} bind:checked={checked14} {...spread} />
|
||||
</div>
|
||||
|
||||
<!-- no support for bind:group; too complex + we may deprecate it in favor of bind:checked={get,set} -->
|
||||
|
||||
<p>Select (single)</p>
|
||||
<!-- select with static checked, value=undefined-->
|
||||
<select bind:value={selected1}>
|
||||
<option value="a">A</option>
|
||||
<option value="b" selected {...spread}>B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<!-- select with dynamic checked, value=undefined-->
|
||||
<select bind:value={selected2}>
|
||||
<option value="a">A</option>
|
||||
<option value="b" selected={defaultChecked}>B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<!-- select with static checked, value=something else than default-->
|
||||
<select bind:value={selected3}>
|
||||
<option value="a">A</option>
|
||||
<option value="b" selected {...spread}>B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<!-- select with dynamic checked, value=something else than default-->
|
||||
<select bind:value={selected4}>
|
||||
<option value="a">A</option>
|
||||
<option value="b" selected={defaultChecked}>B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<p>Select (multiple)</p>
|
||||
<!-- There's no possibility to have the selected attribute influence a multi select initially,
|
||||
because we require the value to be an array -->
|
||||
|
||||
<!-- select with static checked, value=something else than default-->
|
||||
<select multiple bind:value={selected5}>
|
||||
<option value="a">A</option>
|
||||
<option value="b" selected {...spread}>B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<!-- select with dynamic checked, value=something else than default-->
|
||||
<select multiple bind:value={selected6}>
|
||||
<option value="a">A</option>
|
||||
<option value="b" selected={defaultChecked}>B</option>
|
||||
<option value="c">C</option>
|
||||
</select>
|
||||
|
||||
<p>Static values</p>
|
||||
<div class="test-14">
|
||||
<input value="x" defaultValue="y" {...spread} />
|
||||
<input type="checkbox" checked defaultChecked={false} {...spread} />
|
||||
<textarea defaultValue="y" {...spread}>x</textarea>
|
||||
</div>
|
||||
|
||||
<input type="reset" value="Reset" />
|
||||
</form>
|
||||
|
||||
<p>
|
||||
Bound values:
|
||||
<span class="test-1">{value1} {value3} {value6} {value8}</span>
|
||||
<span class="test-2">{value9} {value12} {value14} {value16}</span>
|
||||
<span class="test-3">{value17} {value20} {value22} {value24}</span>
|
||||
<span class="test-4">{checked2} {checked4}</span>
|
||||
<span class="test-5">{checked6} {checked8}</span>
|
||||
<span class="test-6">{checked10} {checked12}</span>
|
||||
<span class="test-7">{checked14}</span>
|
||||
<span class="test-8">{selected1}</span>
|
||||
<span class="test-9">{selected2}</span>
|
||||
<span class="test-10">{selected3}</span>
|
||||
<span class="test-11">{selected4}</span>
|
||||
<span class="test-12">{selected5}</span>
|
||||
<span class="test-13">{selected6}</span>
|
||||
</p>
|
||||
@ -0,0 +1,21 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
mode: ['hydrate'],
|
||||
|
||||
server_props: {
|
||||
browser: false
|
||||
},
|
||||
|
||||
props: {
|
||||
browser: true
|
||||
},
|
||||
|
||||
test({ assert, target }) {
|
||||
assert.equal(target.querySelector('link')?.getAttribute('href'), '/bar');
|
||||
},
|
||||
|
||||
warnings: [
|
||||
'The `href` attribute on `<link xmlns="http://www.w3.org/1999/xhtml" href="/bar" />` changed its value between server and client renders. The client value, `/foo`, will be ignored in favour of the server value'
|
||||
]
|
||||
});
|
||||
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
let { browser } = $props();
|
||||
</script>
|
||||
|
||||
<link href={browser ? '/foo' : '/bar'} />
|
||||
@ -0,0 +1,9 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
test({ assert, target }) {
|
||||
const [input] = target.querySelectorAll('input');
|
||||
|
||||
assert.equal(input.disabled, true);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<iNPUT dIsAbLeD />
|
||||
@ -0,0 +1,32 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
// <option value> is special because falsy values should result in an empty string value attribute
|
||||
export default test({
|
||||
mode: ['client'],
|
||||
test({ assert, target }) {
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<select>
|
||||
<option value="">Default</option>
|
||||
</select>
|
||||
|
||||
<select>
|
||||
<option value="">Default</option>
|
||||
</select>
|
||||
|
||||
<select>
|
||||
<option value="">Default</option>
|
||||
</select>
|
||||
|
||||
<select>
|
||||
<option value="">Default</option>
|
||||
</select>
|
||||
|
||||
<select>
|
||||
<option value="">Default</option>
|
||||
</select>
|
||||
`
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,26 @@
|
||||
<script>
|
||||
let nonreactive = undefined;
|
||||
let reactive = $state();
|
||||
let nonreactive_spread = { value: undefined };
|
||||
let reactive_spread = $state({ value: undefined });
|
||||
</script>
|
||||
|
||||
<select>
|
||||
<option value={undefined}>Default</option>
|
||||
</select>
|
||||
|
||||
<select>
|
||||
<option value={nonreactive}>Default</option>
|
||||
</select>
|
||||
|
||||
<select>
|
||||
<option value={reactive}>Default</option>
|
||||
</select>
|
||||
|
||||
<select>
|
||||
<option {...nonreactive_spread}>Default</option>
|
||||
</select>
|
||||
|
||||
<select>
|
||||
<option {...reactive_spread}>Default</option>
|
||||
</select>
|
||||
@ -0,0 +1,18 @@
|
||||
import { flushSync } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
test({ assert, component, target }) {
|
||||
const options = target.querySelectorAll('option');
|
||||
|
||||
assert.equal(options[0].selected, true);
|
||||
assert.equal(options[1].selected, false);
|
||||
|
||||
// Shouldn't change the value because the value is not bound.
|
||||
component.attrs = { value: ['2'] };
|
||||
flushSync();
|
||||
|
||||
assert.equal(options[0].selected, false);
|
||||
assert.equal(options[1].selected, true);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
import Select from './select.svelte';
|
||||
|
||||
let { attrs = { value: ['1'] } } = $props();
|
||||
</script>
|
||||
|
||||
<Select {attrs} />
|
||||
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
let { attrs } = $props();
|
||||
</script>
|
||||
|
||||
<select multiple {...attrs}>
|
||||
<option value="1">option 1</option>
|
||||
<option value="2">option 2</option>
|
||||
</select>
|
||||
@ -0,0 +1,46 @@
|
||||
import { flushSync } from 'svelte';
|
||||
import { ok, test } from '../../test';
|
||||
|
||||
export default test({
|
||||
test({ assert, component, target, window }) {
|
||||
const [input1, input2] = target.querySelectorAll('input');
|
||||
const select = target.querySelector('select');
|
||||
ok(select);
|
||||
const [option1, option2] = /** @type {NodeListOf<HTMLOptionElement>} */ (select.childNodes);
|
||||
|
||||
let selections = Array.from(select.selectedOptions);
|
||||
assert.equal(selections.length, 2);
|
||||
assert.ok(selections.includes(option1));
|
||||
assert.ok(selections.includes(option2));
|
||||
|
||||
const event = new window.Event('change');
|
||||
|
||||
input1.checked = false;
|
||||
input1.dispatchEvent(event);
|
||||
flushSync();
|
||||
|
||||
selections = Array.from(select.selectedOptions);
|
||||
assert.equal(selections.length, 1);
|
||||
assert.ok(!selections.includes(option1));
|
||||
assert.ok(selections.includes(option2));
|
||||
|
||||
input2.checked = false;
|
||||
input2.dispatchEvent(event);
|
||||
flushSync();
|
||||
input1.checked = true;
|
||||
input1.dispatchEvent(event);
|
||||
flushSync();
|
||||
selections = Array.from(select.selectedOptions);
|
||||
assert.equal(selections.length, 1);
|
||||
assert.ok(selections.includes(option1));
|
||||
assert.ok(!selections.includes(option2));
|
||||
|
||||
component.spread = { value: ['Hello', 'World'] };
|
||||
flushSync();
|
||||
|
||||
selections = Array.from(select.selectedOptions);
|
||||
assert.equal(selections.length, 2);
|
||||
assert.ok(selections.includes(option1));
|
||||
assert.ok(selections.includes(option2));
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
let { spread } = $props();
|
||||
let value = $state(['Hello', 'World']);
|
||||
</script>
|
||||
|
||||
<select multiple {value} {...spread}>
|
||||
<option>Hello</option>
|
||||
<option>World</option>
|
||||
</select>
|
||||
|
||||
<input type="checkbox" value="Hello" bind:group={value}>
|
||||
<input type="checkbox" value="World" bind:group={value}>
|
||||
@ -0,0 +1,3 @@
|
||||
<div title="${inject} world"></div>
|
||||
<div title="`backtick world"></div>
|
||||
<div title="back\slash world"></div>
|
||||
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
export let value = 'world';
|
||||
</script>
|
||||
|
||||
<div title="${inject} {value}"></div>
|
||||
<div title="`backtick {value}"></div>
|
||||
<div title="back\slash {value}"></div>
|
||||
@ -0,0 +1,3 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({ compileOptions: { experimental: { async: true } } });
|
||||
@ -0,0 +1,201 @@
|
||||
import 'svelte/internal/disclose-version';
|
||||
import 'svelte/internal/flags/async';
|
||||
import * as $ from 'svelte/internal/client';
|
||||
|
||||
var root = $.from_html(`<!> <!> <!> <!> <!>`, 1);
|
||||
|
||||
export default function Async_if_chain($$anchor) {
|
||||
function complex1() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let foo = true;
|
||||
var blocking;
|
||||
var $$promises = $.run([async () => blocking = await $.async_derived(() => foo)]);
|
||||
var fragment = root();
|
||||
var node = $.first_child(fragment);
|
||||
|
||||
$.async(node, [$$promises[0]], void 0, (node) => {
|
||||
var consequent = ($$anchor) => {
|
||||
var text = $.text('foo');
|
||||
|
||||
$.append($$anchor, text);
|
||||
};
|
||||
|
||||
var consequent_1 = ($$anchor) => {
|
||||
var text_1 = $.text('bar');
|
||||
|
||||
$.append($$anchor, text_1);
|
||||
};
|
||||
|
||||
var alternate = ($$anchor) => {
|
||||
var text_2 = $.text('else');
|
||||
|
||||
$.append($$anchor, text_2);
|
||||
};
|
||||
|
||||
$.if(node, ($$render) => {
|
||||
if (foo) $$render(consequent); else if (bar) $$render(consequent_1, 1); else $$render(alternate, false);
|
||||
});
|
||||
});
|
||||
|
||||
var node_1 = $.sibling(node, 2);
|
||||
|
||||
$.async(node_1, [$$promises[0]], [() => foo], (node_1, $$condition) => {
|
||||
var consequent_2 = ($$anchor) => {
|
||||
var text_3 = $.text('foo');
|
||||
|
||||
$.append($$anchor, text_3);
|
||||
};
|
||||
|
||||
var consequent_3 = ($$anchor) => {
|
||||
var text_4 = $.text('bar');
|
||||
|
||||
$.append($$anchor, text_4);
|
||||
};
|
||||
|
||||
var alternate_2 = ($$anchor) => {
|
||||
var fragment_1 = $.comment();
|
||||
var node_2 = $.first_child(fragment_1);
|
||||
|
||||
$.async(node_2, [], [() => baz], (node_2, $$condition) => {
|
||||
var consequent_4 = ($$anchor) => {
|
||||
var text_5 = $.text('baz');
|
||||
|
||||
$.append($$anchor, text_5);
|
||||
};
|
||||
|
||||
var alternate_1 = ($$anchor) => {
|
||||
var text_6 = $.text('else');
|
||||
|
||||
$.append($$anchor, text_6);
|
||||
};
|
||||
|
||||
$.if(
|
||||
node_2,
|
||||
($$render) => {
|
||||
if ($.get($$condition)) $$render(consequent_4); else $$render(alternate_1, false);
|
||||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
$.append($$anchor, fragment_1);
|
||||
};
|
||||
|
||||
$.if(node_1, ($$render) => {
|
||||
if ($.get($$condition)) $$render(consequent_2); else if (bar) $$render(consequent_3, 1); else $$render(alternate_2, false);
|
||||
});
|
||||
});
|
||||
|
||||
var node_3 = $.sibling(node_1, 2);
|
||||
|
||||
$.async(node_3, [$$promises[0]], [async () => (await $.save(foo))() > 10], (node_3, $$condition) => {
|
||||
var consequent_5 = ($$anchor) => {
|
||||
var text_7 = $.text('foo');
|
||||
|
||||
$.append($$anchor, text_7);
|
||||
};
|
||||
|
||||
var consequent_6 = ($$anchor) => {
|
||||
var text_8 = $.text('bar');
|
||||
|
||||
$.append($$anchor, text_8);
|
||||
};
|
||||
|
||||
var alternate_4 = ($$anchor) => {
|
||||
var fragment_2 = $.comment();
|
||||
var node_4 = $.first_child(fragment_2);
|
||||
|
||||
$.async(node_4, [$$promises[0]], [async () => (await $.save(foo))() > 5], (node_4, $$condition) => {
|
||||
var consequent_7 = ($$anchor) => {
|
||||
var text_9 = $.text('baz');
|
||||
|
||||
$.append($$anchor, text_9);
|
||||
};
|
||||
|
||||
var alternate_3 = ($$anchor) => {
|
||||
var text_10 = $.text('else');
|
||||
|
||||
$.append($$anchor, text_10);
|
||||
};
|
||||
|
||||
$.if(
|
||||
node_4,
|
||||
($$render) => {
|
||||
if ($.get($$condition)) $$render(consequent_7); else $$render(alternate_3, false);
|
||||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
$.append($$anchor, fragment_2);
|
||||
};
|
||||
|
||||
$.if(node_3, ($$render) => {
|
||||
if ($.get($$condition)) $$render(consequent_5); else if (bar) $$render(consequent_6, 1); else $$render(alternate_4, false);
|
||||
});
|
||||
});
|
||||
|
||||
var node_5 = $.sibling(node_3, 2);
|
||||
|
||||
{
|
||||
var consequent_8 = ($$anchor) => {
|
||||
var text_11 = $.text('foo');
|
||||
|
||||
$.append($$anchor, text_11);
|
||||
};
|
||||
|
||||
var consequent_9 = ($$anchor) => {
|
||||
var text_12 = $.text('bar');
|
||||
|
||||
$.append($$anchor, text_12);
|
||||
};
|
||||
|
||||
var consequent_10 = ($$anchor) => {
|
||||
var text_13 = $.text('baz');
|
||||
|
||||
$.append($$anchor, text_13);
|
||||
};
|
||||
|
||||
var d = $.derived(() => complex1() * complex2 > 100);
|
||||
|
||||
var alternate_5 = ($$anchor) => {
|
||||
var text_14 = $.text('else');
|
||||
|
||||
$.append($$anchor, text_14);
|
||||
};
|
||||
|
||||
$.if(node_5, ($$render) => {
|
||||
if (simple1) $$render(consequent_8); else if (simple2 > 10) $$render(consequent_9, 1); else if ($.get(d)) $$render(consequent_10, 2); else $$render(alternate_5, false);
|
||||
});
|
||||
}
|
||||
|
||||
var node_6 = $.sibling(node_5, 2);
|
||||
|
||||
$.async(node_6, [$$promises[0]], void 0, (node_6) => {
|
||||
var consequent_11 = ($$anchor) => {
|
||||
var text_15 = $.text('foo');
|
||||
|
||||
$.append($$anchor, text_15);
|
||||
};
|
||||
|
||||
var consequent_12 = ($$anchor) => {
|
||||
var text_16 = $.text('bar');
|
||||
|
||||
$.append($$anchor, text_16);
|
||||
};
|
||||
|
||||
var alternate_6 = ($$anchor) => {
|
||||
var text_17 = $.text('else');
|
||||
|
||||
$.append($$anchor, text_17);
|
||||
};
|
||||
|
||||
$.if(node_6, ($$render) => {
|
||||
if ($.get(blocking) > 10) $$render(consequent_11); else if ($.get(blocking) > 5) $$render(consequent_12, 1); else $$render(alternate_6, false);
|
||||
});
|
||||
});
|
||||
|
||||
$.append($$anchor, fragment);
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
import 'svelte/internal/flags/async';
|
||||
import * as $ from 'svelte/internal/server';
|
||||
|
||||
export default function Async_if_chain($$renderer) {
|
||||
function complex1() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let foo = true;
|
||||
var blocking;
|
||||
var $$promises = $$renderer.run([async () => blocking = await foo]);
|
||||
|
||||
$$renderer.async_block([$$promises[0]], ($$renderer) => {
|
||||
if (foo) {
|
||||
$$renderer.push('<!--[-->');
|
||||
$$renderer.push(`foo`);
|
||||
} else if (bar) {
|
||||
$$renderer.push('<!--[1-->');
|
||||
$$renderer.push(`bar`);
|
||||
} else {
|
||||
$$renderer.push('<!--[!-->');
|
||||
$$renderer.push(`else`);
|
||||
}
|
||||
});
|
||||
|
||||
$$renderer.push(`<!--]--> `);
|
||||
|
||||
$$renderer.async_block([$$promises[0]], async ($$renderer) => {
|
||||
if ((await $.save(foo))()) {
|
||||
$$renderer.push('<!--[-->');
|
||||
$$renderer.push(`foo`);
|
||||
} else if (bar) {
|
||||
$$renderer.push('<!--[1-->');
|
||||
$$renderer.push(`bar`);
|
||||
} else {
|
||||
$$renderer.push('<!--[!-->');
|
||||
|
||||
$$renderer.child_block(async ($$renderer) => {
|
||||
if ((await $.save(baz))()) {
|
||||
$$renderer.push('<!--[-->');
|
||||
$$renderer.push(`baz`);
|
||||
} else {
|
||||
$$renderer.push('<!--[!-->');
|
||||
$$renderer.push(`else`);
|
||||
}
|
||||
});
|
||||
|
||||
$$renderer.push(`<!--]-->`);
|
||||
}
|
||||
});
|
||||
|
||||
$$renderer.push(`<!--]--> `);
|
||||
|
||||
$$renderer.async_block([$$promises[0]], async ($$renderer) => {
|
||||
if ((await $.save(foo))() > 10) {
|
||||
$$renderer.push('<!--[-->');
|
||||
$$renderer.push(`foo`);
|
||||
} else if (bar) {
|
||||
$$renderer.push('<!--[1-->');
|
||||
$$renderer.push(`bar`);
|
||||
} else {
|
||||
$$renderer.push('<!--[!-->');
|
||||
|
||||
$$renderer.async_block([$$promises[0]], async ($$renderer) => {
|
||||
if ((await $.save(foo))() > 5) {
|
||||
$$renderer.push('<!--[-->');
|
||||
$$renderer.push(`baz`);
|
||||
} else {
|
||||
$$renderer.push('<!--[!-->');
|
||||
$$renderer.push(`else`);
|
||||
}
|
||||
});
|
||||
|
||||
$$renderer.push(`<!--]-->`);
|
||||
}
|
||||
});
|
||||
|
||||
$$renderer.push(`<!--]--> `);
|
||||
|
||||
if (simple1) {
|
||||
$$renderer.push('<!--[-->');
|
||||
$$renderer.push(`foo`);
|
||||
} else if (simple2 > 10) {
|
||||
$$renderer.push('<!--[1-->');
|
||||
$$renderer.push(`bar`);
|
||||
} else if (complex1() * complex2 > 100) {
|
||||
$$renderer.push('<!--[2-->');
|
||||
$$renderer.push(`baz`);
|
||||
} else {
|
||||
$$renderer.push('<!--[!-->');
|
||||
$$renderer.push(`else`);
|
||||
}
|
||||
|
||||
$$renderer.push(`<!--]--> `);
|
||||
|
||||
$$renderer.async_block([$$promises[0]], ($$renderer) => {
|
||||
if (blocking > 10) {
|
||||
$$renderer.push('<!--[-->');
|
||||
$$renderer.push(`foo`);
|
||||
} else if (blocking > 5) {
|
||||
$$renderer.push('<!--[1-->');
|
||||
$$renderer.push(`bar`);
|
||||
} else {
|
||||
$$renderer.push('<!--[!-->');
|
||||
$$renderer.push(`else`);
|
||||
}
|
||||
});
|
||||
|
||||
$$renderer.push(`<!--]-->`);
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
<script>
|
||||
function complex1() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let foo = $state(true);
|
||||
let blocking = $derived(await foo);
|
||||
</script>
|
||||
|
||||
<!-- simple chain - should have no nested $.if() -->
|
||||
{#if foo}
|
||||
foo
|
||||
{:else if bar}
|
||||
bar
|
||||
{:else}
|
||||
else
|
||||
{/if}
|
||||
|
||||
<!-- simple chain with await expressions - should have $.if() at each await expression -->
|
||||
{#if await foo}
|
||||
foo
|
||||
{:else if bar}
|
||||
bar
|
||||
{:else if await baz}
|
||||
baz
|
||||
{:else}
|
||||
else
|
||||
{/if}
|
||||
|
||||
<!-- simple chain with await expressions #2 - should have $.if() at each await expression (ideally we can detect that await foo is unnecessary to await multiple times and this is one $.if()) -->
|
||||
{#if await foo > 10}
|
||||
foo
|
||||
{:else if bar}
|
||||
bar
|
||||
{:else if await foo > 5}
|
||||
baz
|
||||
{:else}
|
||||
else
|
||||
{/if}
|
||||
|
||||
<!-- simple chain with some expressions that cause a $.derived - should be one $.if() -->
|
||||
{#if simple1}
|
||||
foo
|
||||
{:else if simple2 > 10}
|
||||
bar
|
||||
{:else if complex1() * complex2 > 100}
|
||||
baz
|
||||
{:else}
|
||||
else
|
||||
{/if}
|
||||
|
||||
<!-- simple chain with blocking expressions - should be one $.if() -->
|
||||
{#if blocking > 10}
|
||||
foo
|
||||
{:else if blocking > 5}
|
||||
bar
|
||||
{:else}
|
||||
else
|
||||
{/if}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue