#if using choose

pull/14551/head
adiguba 2 years ago
parent b97c659587
commit 3e4552ece8

@ -3,94 +3,63 @@
/** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js';
/**
* @param {AST.IfBlock} node
* @param {ComponentContext} context
* @param {number} index
*/
export function IfBlock(node, context) {
context.state.template.push_quasi('<!>');
// const conditions = [ ]
/** @type {AST.IfBlock} */
let if_block = node;
/** @type {ArrowFunctionExpression[]} */
let tests = [];
/** @type {ArrowFunctionExpression[]} */
let consequents = [];
/** @type {Expression[]} */
const args = [context.state.node, b.array(tests), b.array(consequents)];
function create_if(node, context, index) {
const test = /** @type {Expression} */ (context.visit(node.test));
const nb = b.literal(index);
const fn = b.arrow(
[b.id('$$anchor')],
/** @type {BlockStatement} */ (context.visit(node.consequent))
);
while (true) {
const test = /** @type {Expression} */ (context.visit(if_block.test));
const consequent = /** @type {BlockStatement} */ (context.visit(if_block.consequent));
tests.push(b.arrow([], test))
consequents.push(b.arrow([b.id('$$anchor')], consequent));
const alternate = if_block.alternate;
if (alternate && alternate.nodes.length === 1 && alternate.nodes[0].type === 'IfBlock' && alternate.nodes[0].elseif) {
if_block = alternate.nodes[0];
} else {
if (alternate) {
args.push( b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(alternate))) );
}
break;
}
}
context.state.init.push(b.stmt(b.call('$.pick', ...args)));
return b.if(test, b.block([b.return(b.array([nb, fn]))]));
}
/**
* @param {AST.IfBlock} node
* @param {ComponentContext} context
*/
export function IfBlockOLD(node, context) {
export function IfBlock(node, context) {
context.state.template.push_quasi('<!>');
const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent));
let index = 0;
const args = [
context.state.node,
b.thunk(/** @type {Expression} */ (context.visit(node.test))),
b.arrow([b.id('$$anchor')], consequent)
];
/** @type {IfStatement} */
const main_if = create_if(node, context, index++);
if (node.alternate || node.elseif) {
args.push(
node.alternate
? b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(node.alternate)))
: b.literal(null)
);
}
/** @type {IfStatement} */
let current_if = main_if;
let alternate = node.alternate;
if (node.elseif) {
// We treat this...
//
// {#if x}
// ...
// {:else}
// {#if y}
// <div transition:foo>...</div>
// {/if}
// {/if}
//
// ...slightly differently to this...
//
// {#if x}
// ...
// {:else if y}
// <div transition:foo>...</div>
// {/if}
//
// ...even though they're logically equivalent. In the first case, the
// transition will only play when `y` changes, but in the second it
// should play when `x` or `y` change — both are considered 'local'
args.push(b.literal(true));
while (
alternate &&
alternate.nodes.length === 1 &&
alternate.nodes[0].type === 'IfBlock' &&
alternate.nodes[0].elseif
) {
const current_node = alternate.nodes[0];
current_if = current_if.alternate = create_if(current_node, context, index++);
alternate = current_node.alternate;
}
if (alternate) {
current_if.alternate = b.block([
b.return(
b.array([
b.literal(index++),
b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(alternate)))
])
)
]);
}
context.state.init.push(b.stmt(b.call('$.if', ...args)));
context.state.init.push(
b.stmt(
b.call('$.choose', context.state.node, b.arrow([], b.block([main_if])), b.literal(index))
)
);
}

@ -5,58 +5,60 @@ import { BLOCK_OPEN_ELSE } from '../../../../../internal/server/hydration.js';
import * as b from '../../../../utils/builders.js';
import { block_close, block_open } from './shared/utils.js';
/**
* @param {AST.IfBlock} node
* @param {ComponentContext} context
* @param {number} index
*/
function create_if(node, context, index) {
const test = /** @type {Expression} */ (context.visit(node.test));
const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent));
consequent.body.unshift(
b.stmt(
b.assignment(
'+=',
b.id('$$payload.out'),
index === 0 ? block_open : b.literal(`<!--[${index}-->`)
)
)
);
return b.if(test, consequent);
}
/**
* @param {AST.IfBlock} node
* @param {ComponentContext} context
*/
export function IfBlock(node, context) {
/** @type {AST.IfBlock} */
let if_block = node;
/** @type {IfStatement | null} */
let first_statement = null;
/** @type {IfStatement | null} */
let previous_statement = null;
let index = 0;
while (true) {
const test = /** @type {Expression} */ (context.visit(if_block.test));
const consequent = /** @type {BlockStatement} */ (context.visit(if_block.consequent));
consequent.body.unshift(b.stmt(b.assignment('+=', b.id('$$payload.out'),
index=== 0 ? block_open : b.literal(`<!--[${index}-->`))));
index++;
const statement = b.if(test, consequent);
/** @type {IfStatement} */
let current_if = create_if(node, context, index++);
if (previous_statement !== null) {
previous_statement.alternate = statement;
} else if (first_statement === null) {
first_statement = statement;
}
previous_statement = statement;
context.state.template.push(current_if, block_close);
const alternate = if_block.alternate;
if (alternate && alternate.nodes.length === 1 && alternate.nodes[0].type === 'IfBlock' && alternate.nodes[0].elseif) {
if_block = alternate.nodes[0];
} else {
statement.alternate = alternate
? /** @type {BlockStatement} */ (context.visit(alternate))
: b.block([]);
const else_index = alternate ? `<!--[${index}-->` : BLOCK_OPEN_ELSE;
statement.alternate.body.unshift(
b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(else_index)))
);
break;
}
}
let alternate = node.alternate;
while (
alternate &&
alternate.nodes.length === 1 &&
alternate.nodes[0].type === 'IfBlock' &&
alternate.nodes[0].elseif
) {
const current_node = alternate.nodes[0];
current_if = current_if.alternate = create_if(current_node, context, index++);
alternate = current_node.alternate;
}
if (first_statement === null) {
throw new Error("missing if"); // should not occurs
/** @type {string} */
let else_key;
if (alternate) {
current_if.alternate = /** @type {BlockStatement} */ (context.visit(alternate));
else_key = `<!--[${index}-->`;
} else {
current_if.alternate = b.block([]);
else_key = BLOCK_OPEN_ELSE;
}
context.state.template.push(first_statement, block_close);
current_if.alternate.body.unshift(
b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(else_key)))
);
}

@ -13,45 +13,37 @@ import { HYDRATION_START_ELSE } from '../../../../constants.js';
import { derived } from '../../reactivity/deriveds.js';
import { get } from '../../runtime.js';
/**
* @param {TemplateNode} node
* @param {(() => boolean)[]} test_fns
* @param {((anchor: Node) => void)[]} consequent_fns
* @param {((anchor: Node) => void) | null} alternate_fn
* @param {() => [number, (anchor: Node) => void] | undefined} get_fn
* @returns {void}
*/
export function pick(node, test_fns, consequent_fns, alternate_fn = null) {
export function choose(node, get_fn) {
if (hydrating) {
hydrate_next();
}
var anchor = node;
const max = consequent_fns.length;
/** @type {{d:Derived<boolean>, e:Effect|null}[]} */
var effects = test_fns.map( fn => ({ d: derived(fn), e: null }) );
if (alternate_fn) {
// @ts-ignore
effects[-1] = { e: null }
}
/** @type {Array<Effect|undefined>} */
const effects = [];
/** @type {number | null} */
var current_index = null;
block(() => {
const previous_index = current_index;
if (current_index === (current_index = (effects.findIndex(e => get(e.d))))) return;
const [new_index, new_fn] = get_fn() ?? [-1, null];
if (current_index === new_index) return;
const old_index = current_index;
current_index = new_index;
/** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
let mismatch = false;
if (hydrating) {
const data = current_index < 0 ? '[!' : (current_index === 0 ? '[' : ('[' + current_index) );
const data = new_index < 0 ? '[!' : new_index === 0 ? '[' : '[' + new_index;
if (data !== /** @type {Comment} */ (anchor).data) {
// Hydration mismatch: remove everything inside the anchor and start fresh.
// This could happen with `{#if browser}...{/if}`, for example
@ -63,38 +55,28 @@ export function pick(node, test_fns, consequent_fns, alternate_fn = null) {
}
}
let current_effect = effects[current_index]?.e;
if (current_effect) {
resume_effect(current_effect);
} else {
const fn = current_index < 0 ? alternate_fn : consequent_fns[current_index];
if (fn) {
let target_anchor = anchor;
if (previous_index != null && current_index !== -1) {
const alternate_effect = effects[-1]?.e;
if (alternate_effect && alternate_effect.nodes_start) {
target_anchor = alternate_effect.nodes_start;
}
for (let i=current_index+1; i<max; i++) {
const effect = effects[i];
if (effect.e && effect.e.nodes_start) {
target_anchor = effect.e.nodes_start;
break;
}
if (effects[new_index]) {
resume_effect(effects[new_index]);
} else if (new_fn) {
let target_anchor = anchor;
if (old_index != -1) {
// search the first node after the current condition :
const len = effects.length;
for (let i = new_index + 1; i < len; i++) {
const effect = effects[i];
if (effect && effect.nodes_start) {
target_anchor = effect.nodes_start;
break;
}
}
current_effect = branch(() => fn(target_anchor));
effects[current_index].e = current_effect;
}
}
effects[new_index] = branch(() => new_fn(target_anchor));
}
if (previous_index!=null) {
const previous_effect = effects[previous_index]?.e;
if (previous_effect) {
pause_effect(previous_effect, () => {
effects[previous_index].e = null;
});
}
if (old_index != null && old_index >= 0 && effects[old_index]) {
pause_effect(effects[old_index], () => {
delete effects[old_index];
});
}
if (mismatch) {
// continue in hydration mode
@ -107,7 +89,6 @@ export function pick(node, test_fns, consequent_fns, alternate_fn = null) {
}
}
/**
* @param {TemplateNode} node
* @param {() => boolean} get_condition

@ -93,8 +93,8 @@ export function remove_nodes() {
if (data === HYDRATION_END) {
if (depth === 0) return node;
depth -= 1;
} else if (data.startsWith('[')) {
// } else if (data === HYDRATION_START || data === HYDRATION_START_ELSE) {
} else if (data.length && data[0] === '[') {
// } else if (data === HYDRATION_START || data === HYDRATION_START_ELSE) {
depth += 1;
}
}

@ -12,7 +12,7 @@ export {
export { check_target, legacy_api } from './dev/legacy.js';
export { inspect } from './dev/inspect.js';
export { await_block as await } from './dom/blocks/await.js';
export { if_block as if, pick } from './dom/blocks/if.js';
export { if_block as if, choose } from './dom/blocks/if.js';
export { key_block as key } from './dom/blocks/key.js';
export { css_props } from './dom/blocks/css-props.js';
export { index, each } from './dom/blocks/each.js';

Loading…
Cancel
Save