mirror of https://github.com/sveltejs/svelte
parent
31e5f8b5de
commit
2cc56af2c6
@ -0,0 +1,32 @@
|
||||
import AbstractBlock from './shared/AbstractBlock';
|
||||
import Component from '../Component';
|
||||
import TemplateScope from './shared/TemplateScope';
|
||||
import { TemplateNode } from '../../interfaces';
|
||||
import ConstTag from './ConstTag';
|
||||
import get_const_tags from './shared/get_const_tags';
|
||||
import Expression from './shared/Expression';
|
||||
import SwitchBlock from './SwitchBlock';
|
||||
|
||||
export default class CaseBlock extends AbstractBlock {
|
||||
type: 'CaseBlock';
|
||||
is_default: boolean;
|
||||
test?: Expression;
|
||||
scope: TemplateScope;
|
||||
const_tags: ConstTag[];
|
||||
|
||||
constructor(component: Component, parent: SwitchBlock, scope: TemplateScope, info: TemplateNode) {
|
||||
super(component, parent, scope, info);
|
||||
this.scope = scope.child();
|
||||
|
||||
this.test = info.test;
|
||||
this.is_default = info.isdefault ?? false;
|
||||
|
||||
if (!this.is_default) {
|
||||
this.test = new Expression(component, this, this.scope, info.test);
|
||||
}
|
||||
|
||||
([this.const_tags, this.children] = get_const_tags(info.children, component, this, this));
|
||||
|
||||
this.warn_if_empty_block();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
import AbstractBlock from './shared/AbstractBlock';
|
||||
import Component from '../Component';
|
||||
import TemplateScope from './shared/TemplateScope';
|
||||
import { TemplateNode } from '../../interfaces';
|
||||
import Node from './shared/Node';
|
||||
import CaseBlock from './CaseBlock';
|
||||
import Expression from './shared/Expression';
|
||||
|
||||
export default class SwitchBlock extends AbstractBlock {
|
||||
type: 'SwitchBlock';
|
||||
cases: CaseBlock[];
|
||||
discriminant: Expression;
|
||||
scope: TemplateScope;
|
||||
|
||||
constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
|
||||
super(component, parent, scope, info);
|
||||
this.scope = scope.child();
|
||||
|
||||
this.discriminant = new Expression(component, this, this.scope, info.disccriminant);
|
||||
|
||||
this.cases = info.cases?.length
|
||||
? info.cases.map(c => new CaseBlock(component, this, scope, c))
|
||||
: [];
|
||||
|
||||
this.warn_if_empty_block();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
import Wrapper from './shared/Wrapper';
|
||||
import Renderer from '../Renderer';
|
||||
import Block from '../Block';
|
||||
import { Identifier } from 'estree';
|
||||
import { push_array } from '../../../utils/push_array';
|
||||
import ConditionalBlockBranch from './shared/ConditionalBlockBranch';
|
||||
import SwitchBlock from '../../nodes/SwitchBlock';
|
||||
import CaseBlock from '../../nodes/CaseBlock';
|
||||
import Expression from '../../nodes/shared/Expression';
|
||||
import ConditionalBlockWrapper from './shared/ConditionalBlockWrapper';
|
||||
|
||||
export default class SwitchBlockWrapper extends ConditionalBlockWrapper {
|
||||
node: SwitchBlock;
|
||||
branches: ConditionalBlockBranch[];
|
||||
discriminant: Expression;
|
||||
needs_update = false;
|
||||
|
||||
var: Identifier = { type: 'Identifier', name: 'switch_block' };
|
||||
|
||||
constructor(
|
||||
renderer: Renderer,
|
||||
block: Block,
|
||||
parent: Wrapper,
|
||||
node: SwitchBlock,
|
||||
strip_whitespace: boolean,
|
||||
next_sibling: Wrapper
|
||||
) {
|
||||
super(renderer, block, parent, node);
|
||||
|
||||
this.discriminant = node.discriminant;
|
||||
|
||||
const blocks: Block[] = [];
|
||||
let is_dynamic = false;
|
||||
let has_intros = false;
|
||||
let has_outros = false;
|
||||
|
||||
const create_branches = (node: CaseBlock) => {
|
||||
const branch = new ConditionalBlockBranch(
|
||||
renderer,
|
||||
block,
|
||||
this,
|
||||
node,
|
||||
strip_whitespace,
|
||||
next_sibling
|
||||
);
|
||||
|
||||
this.branches.push(branch);
|
||||
|
||||
blocks.push(branch.block);
|
||||
|
||||
if (node.is_default) {
|
||||
block.add_dependencies(this.node.discriminant.dependencies);
|
||||
} else block.add_dependencies(node.test.dependencies);
|
||||
|
||||
if (branch.block.dependencies.size > 0) {
|
||||
// the condition, or its contents, is dynamic
|
||||
is_dynamic = true;
|
||||
block.add_dependencies(branch.block.dependencies);
|
||||
}
|
||||
|
||||
if (branch.dependencies && branch.dependencies.length > 0) {
|
||||
// the condition itself is dynamic
|
||||
this.needs_update = true;
|
||||
}
|
||||
|
||||
if (branch.block.has_intros) has_intros = true;
|
||||
if (branch.block.has_outros) has_outros = true;
|
||||
};
|
||||
|
||||
this.node.cases.forEach((c) => create_branches(c));
|
||||
|
||||
// the default statement is first in the markup
|
||||
// but logically we want it to be last
|
||||
if ((this.branches[0].node as CaseBlock).is_default) {
|
||||
const [default_case] = this.branches.splice(0, 1);
|
||||
this.branches.push(default_case);
|
||||
}
|
||||
|
||||
blocks.forEach((block) => {
|
||||
block.has_update_method = is_dynamic;
|
||||
block.has_intro_method = has_intros;
|
||||
block.has_outro_method = has_outros;
|
||||
});
|
||||
|
||||
push_array(renderer.blocks, blocks);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
import { walk } from 'estree-walker';
|
||||
import CaseBlock from '../../../nodes/CaseBlock';
|
||||
import ElseBlock from '../../../nodes/ElseBlock';
|
||||
import IfBlock from '../../../nodes/IfBlock';
|
||||
import Block from '../../Block';
|
||||
import Renderer from '../../Renderer';
|
||||
import FragmentWrapper from '../Fragment';
|
||||
import { add_const_tags_context } from './add_const_tags';
|
||||
import create_debugging_comment from './create_debugging_comment';
|
||||
import Wrapper from './Wrapper';
|
||||
import { Node } from 'estree';
|
||||
import Expression from '../../../nodes/shared/Expression';
|
||||
import ConditionalBlockWrapper from './ConditionalBlockWrapper';
|
||||
|
||||
function get_expression(node: IfBlock | ElseBlock | CaseBlock): Expression | void {
|
||||
switch (node.type) {
|
||||
case 'ElseBlock':
|
||||
return;
|
||||
|
||||
case 'IfBlock':
|
||||
return node.expression;
|
||||
|
||||
case 'CaseBlock':
|
||||
if (!node.is_default) return node.test;
|
||||
}
|
||||
}
|
||||
|
||||
export default class ConditionalBlockBranch extends Wrapper {
|
||||
block: Block;
|
||||
fragment: FragmentWrapper;
|
||||
dependencies?: string[];
|
||||
condition?: any;
|
||||
snippet?: Node;
|
||||
is_dynamic: boolean;
|
||||
node: IfBlock | ElseBlock | CaseBlock;
|
||||
|
||||
var = null;
|
||||
get_ctx_name: Node | undefined;
|
||||
|
||||
constructor(
|
||||
renderer: Renderer,
|
||||
block: Block,
|
||||
parent: ConditionalBlockWrapper,
|
||||
node: IfBlock | ElseBlock | CaseBlock,
|
||||
strip_whitespace: boolean,
|
||||
next_sibling: Wrapper
|
||||
) {
|
||||
super(renderer, block, parent, node);
|
||||
|
||||
const expression = get_expression(node);
|
||||
const type =
|
||||
node.type === 'CaseBlock'
|
||||
? 'case'
|
||||
: node.type === 'ElseBlock'
|
||||
? 'else'
|
||||
: 'if';
|
||||
|
||||
if (expression) {
|
||||
this.dependencies = expression.dynamic_dependencies();
|
||||
|
||||
// TODO is this the right rule? or should any non-reference count?
|
||||
// const should_cache = !is_reference(expression.node, null) && dependencies.length > 0;
|
||||
let should_cache = false;
|
||||
walk(expression.node, {
|
||||
enter(node) {
|
||||
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
||||
should_cache = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (should_cache) {
|
||||
this.condition = block.get_unique_name('conditional_render');
|
||||
this.snippet = expression.manipulate(block) as Node;
|
||||
} else {
|
||||
this.condition = expression.manipulate(block);
|
||||
}
|
||||
}
|
||||
|
||||
add_const_tags_context(renderer, this.node.const_tags);
|
||||
|
||||
this.block = block.child({
|
||||
comment: create_debugging_comment(node, parent.renderer.component),
|
||||
name: parent.renderer.component.get_unique_name(
|
||||
type === 'case'
|
||||
? 'create_case_block'
|
||||
: type === 'else'
|
||||
? 'create_else_block'
|
||||
: 'create_if_block'
|
||||
),
|
||||
type
|
||||
});
|
||||
|
||||
this.fragment = new FragmentWrapper(
|
||||
renderer,
|
||||
this.block,
|
||||
node.children,
|
||||
parent,
|
||||
strip_whitespace,
|
||||
next_sibling
|
||||
);
|
||||
|
||||
this.is_dynamic = this.block.dependencies.size > 0;
|
||||
|
||||
if (node.const_tags.length > 0) {
|
||||
this.get_ctx_name = parent.renderer.component.get_unique_name(
|
||||
type === 'case'
|
||||
? 'get_case_ctx'
|
||||
: type === 'else'
|
||||
? 'get_else_ctx'
|
||||
: 'get_if_ctx'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,530 @@
|
||||
import { b, x } from 'code-red';
|
||||
import { is_head } from './is_head';
|
||||
import { Identifier } from 'estree';
|
||||
import { add_const_tags } from './add_const_tags';
|
||||
import ConditionalBlockBranch from './ConditionalBlockBranch';
|
||||
import SwitchBlock from '../../../nodes/SwitchBlock';
|
||||
import Wrapper from './Wrapper';
|
||||
import IfBlock from '../../../nodes/IfBlock';
|
||||
import Renderer from '../../Renderer';
|
||||
import Block from '../../Block';
|
||||
|
||||
export default class ConditionalBlockWrapper extends Wrapper {
|
||||
node: IfBlock | SwitchBlock;
|
||||
branches: ConditionalBlockBranch[];
|
||||
needs_update = false;
|
||||
|
||||
var: Identifier = { type: 'Identifier', name: 'if_block' };
|
||||
|
||||
constructor(
|
||||
renderer: Renderer,
|
||||
block: Block,
|
||||
parent: Wrapper,
|
||||
node: IfBlock | SwitchBlock
|
||||
) {
|
||||
super(renderer, block, parent, node);
|
||||
|
||||
this.cannot_use_innerhtml();
|
||||
this.not_static_content();
|
||||
|
||||
this.branches = [];
|
||||
}
|
||||
|
||||
render(
|
||||
block: Block,
|
||||
parent_node: Identifier,
|
||||
parent_nodes: Identifier
|
||||
) {
|
||||
const name = this.var;
|
||||
|
||||
const needs_anchor = this.next ? !this.next.is_dom_node() : !parent_node || !this.parent.is_dom_node();
|
||||
const anchor = needs_anchor
|
||||
? block.get_unique_name(`${this.var.name}_anchor`)
|
||||
: (this.next && this.next.var) || 'null';
|
||||
|
||||
const has_else = !(this.branches[this.branches.length - 1].condition);
|
||||
const if_exists_condition = has_else ? null : name;
|
||||
|
||||
const dynamic = this.branches[0].block.has_update_method; // can use [0] as proxy for all, since they necessarily have the same value
|
||||
const has_intros = this.branches[0].block.has_intro_method;
|
||||
const has_outros = this.branches[0].block.has_outro_method;
|
||||
const has_transitions = has_intros || has_outros;
|
||||
|
||||
this.branches.forEach(branch => {
|
||||
if (branch.get_ctx_name) {
|
||||
this.renderer.blocks.push(b`
|
||||
function ${branch.get_ctx_name}(#ctx) {
|
||||
const child_ctx = #ctx.slice();
|
||||
${add_const_tags(block, branch.node.const_tags, 'child_ctx')}
|
||||
return child_ctx;
|
||||
}
|
||||
`);
|
||||
}
|
||||
});
|
||||
|
||||
const vars = { name, anchor, if_exists_condition, has_else, has_transitions };
|
||||
|
||||
const detaching = parent_node && !is_head(parent_node) ? null : 'detaching';
|
||||
|
||||
if (this.node.type === 'IfBlock' && this.node.else) {
|
||||
this.branches.forEach(branch => {
|
||||
if (branch.snippet) block.add_variable(branch.condition);
|
||||
});
|
||||
|
||||
if (has_outros) {
|
||||
this.render_compound_with_outros(block, parent_node, parent_nodes, dynamic, vars, detaching);
|
||||
|
||||
block.chunks.outro.push(b`@transition_out(${name});`);
|
||||
} else {
|
||||
this.render_compound(block, parent_node, parent_nodes, dynamic, vars, detaching);
|
||||
}
|
||||
} else {
|
||||
this.render_simple(block, parent_node, parent_nodes, dynamic, vars, detaching);
|
||||
|
||||
if (has_outros) {
|
||||
block.chunks.outro.push(b`@transition_out(${name});`);
|
||||
}
|
||||
}
|
||||
|
||||
if (if_exists_condition) {
|
||||
block.chunks.create.push(b`if (${if_exists_condition}) ${name}.c();`);
|
||||
} else {
|
||||
block.chunks.create.push(b`${name}.c();`);
|
||||
}
|
||||
|
||||
if (parent_nodes && this.renderer.options.hydratable) {
|
||||
if (if_exists_condition) {
|
||||
block.chunks.claim.push(
|
||||
b`if (${if_exists_condition}) ${name}.l(${parent_nodes});`
|
||||
);
|
||||
} else {
|
||||
block.chunks.claim.push(
|
||||
b`${name}.l(${parent_nodes});`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_intros || has_outros) {
|
||||
block.chunks.intro.push(b`@transition_in(${name});`);
|
||||
}
|
||||
|
||||
if (needs_anchor) {
|
||||
block.add_element(
|
||||
anchor as Identifier,
|
||||
x`@empty()`,
|
||||
parent_nodes && x`@empty()`,
|
||||
parent_node
|
||||
);
|
||||
}
|
||||
|
||||
this.branches.forEach(branch => {
|
||||
branch.fragment.render(branch.block, null, x`#nodes` as unknown as Identifier);
|
||||
});
|
||||
}
|
||||
|
||||
render_compound(
|
||||
block: Block,
|
||||
parent_node: Identifier,
|
||||
_parent_nodes: Identifier,
|
||||
dynamic,
|
||||
{ name, anchor, has_else, if_exists_condition, has_transitions },
|
||||
detaching
|
||||
) {
|
||||
const select_block_type = this.renderer.component.get_unique_name('select_block_type');
|
||||
const current_block_type = block.get_unique_name('current_block_type');
|
||||
const need_select_block_ctx = this.branches.some(branch => branch.get_ctx_name);
|
||||
const select_block_ctx = need_select_block_ctx ? block.get_unique_name('select_block_ctx') : null;
|
||||
const if_ctx = select_block_ctx ? x`${select_block_ctx}(#ctx, ${current_block_type})` : x`#ctx`;
|
||||
|
||||
const get_block = has_else
|
||||
? x`${current_block_type}(${if_ctx})`
|
||||
: x`${current_block_type} && ${current_block_type}(${if_ctx})`;
|
||||
|
||||
if (this.needs_update) {
|
||||
block.chunks.init.push(b`
|
||||
function ${select_block_type}(#ctx, #dirty) {
|
||||
${this.branches.map(({ dependencies, condition, snippet }) => {
|
||||
return b`${snippet && dependencies.length > 0 ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = null;` : null}`;
|
||||
})}
|
||||
${this.branches.map(({ condition, snippet, block }) => condition
|
||||
? b`
|
||||
${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`}
|
||||
if (${condition}) return ${block.name};`
|
||||
: b`return ${block.name};`
|
||||
)}
|
||||
}
|
||||
`);
|
||||
} else {
|
||||
block.chunks.init.push(b`
|
||||
function ${select_block_type}(#ctx, #dirty) {
|
||||
${this.branches.map(({ condition, snippet, block }) => condition
|
||||
? b`if (${snippet || condition}) return ${block.name};`
|
||||
: b`return ${block.name};`)}
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
if (need_select_block_ctx) {
|
||||
// if all branches needs create a context
|
||||
if (this.branches.every(branch => branch.get_ctx_name)) {
|
||||
block.chunks.init.push(b`
|
||||
function ${select_block_ctx}(#ctx, #type) {
|
||||
${this.branches.map(({ condition, get_ctx_name, block }) => {
|
||||
return condition
|
||||
? b`if (#type === ${block.name}) return ${get_ctx_name}(#ctx);`
|
||||
: b`return ${get_ctx_name}(#ctx);`;
|
||||
}).filter(Boolean)}
|
||||
}
|
||||
`);
|
||||
} else {
|
||||
// when not all branches need to create a new context,
|
||||
// this code is simpler
|
||||
block.chunks.init.push(b`
|
||||
function ${select_block_ctx}(#ctx, #type) {
|
||||
${this.branches.map(({ get_ctx_name, block }) => {
|
||||
return get_ctx_name
|
||||
? b`if (#type === ${block.name}) return ${get_ctx_name}(#ctx);`
|
||||
: null;
|
||||
}).filter(Boolean)}
|
||||
return #ctx;
|
||||
}
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
block.chunks.init.push(b`
|
||||
let ${current_block_type} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()});
|
||||
let ${name} = ${get_block};
|
||||
`);
|
||||
|
||||
const initial_mount_node = parent_node || '#target';
|
||||
const anchor_node = parent_node ? 'null' : '#anchor';
|
||||
|
||||
if (if_exists_condition) {
|
||||
block.chunks.mount.push(
|
||||
b`if (${if_exists_condition}) ${name}.m(${initial_mount_node}, ${anchor_node});`
|
||||
);
|
||||
} else {
|
||||
block.chunks.mount.push(
|
||||
b`${name}.m(${initial_mount_node}, ${anchor_node});`
|
||||
);
|
||||
}
|
||||
|
||||
if (this.needs_update) {
|
||||
const update_mount_node = this.get_update_mount_node(anchor);
|
||||
|
||||
const change_block = b`
|
||||
${if_exists_condition ? b`if (${if_exists_condition}) ${name}.d(1)` : b`${name}.d(1)`};
|
||||
${name} = ${get_block};
|
||||
if (${name}) {
|
||||
${name}.c();
|
||||
${has_transitions && b`@transition_in(${name}, 1);`}
|
||||
${name}.m(${update_mount_node}, ${anchor});
|
||||
}
|
||||
`;
|
||||
|
||||
if (dynamic) {
|
||||
block.chunks.update.push(b`
|
||||
if (${current_block_type} === (${current_block_type} = ${select_block_type}(#ctx, #dirty)) && ${name}) {
|
||||
${name}.p(${if_ctx}, #dirty);
|
||||
} else {
|
||||
${change_block}
|
||||
}
|
||||
`);
|
||||
} else {
|
||||
block.chunks.update.push(b`
|
||||
if (${current_block_type} !== (${current_block_type} = ${select_block_type}(#ctx, #dirty))) {
|
||||
${change_block}
|
||||
}
|
||||
`);
|
||||
}
|
||||
} else if (dynamic) {
|
||||
if (if_exists_condition) {
|
||||
block.chunks.update.push(b`if (${if_exists_condition}) ${name}.p(${if_ctx}, #dirty);`);
|
||||
} else {
|
||||
block.chunks.update.push(b`${name}.p(${if_ctx}, #dirty);`);
|
||||
}
|
||||
}
|
||||
|
||||
if (if_exists_condition) {
|
||||
block.chunks.destroy.push(b`
|
||||
if (${if_exists_condition}) {
|
||||
${name}.d(${detaching});
|
||||
}
|
||||
`);
|
||||
} else {
|
||||
block.chunks.destroy.push(b`
|
||||
${name}.d(${detaching});
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
// if any of the siblings have outros, we need to keep references to the blocks
|
||||
// (TODO does this only apply to bidi transitions?)
|
||||
render_compound_with_outros(
|
||||
block: Block,
|
||||
parent_node: Identifier,
|
||||
_parent_nodes: Identifier,
|
||||
dynamic,
|
||||
{ name, anchor, has_else, has_transitions, if_exists_condition },
|
||||
detaching
|
||||
) {
|
||||
const select_block_type = this.renderer.component.get_unique_name('select_block_type');
|
||||
const current_block_type_index = block.get_unique_name('current_block_type_index');
|
||||
const previous_block_index = block.get_unique_name('previous_block_index');
|
||||
const if_block_creators = block.get_unique_name('if_block_creators');
|
||||
const if_blocks = block.get_unique_name('if_blocks');
|
||||
const need_select_block_ctx = this.branches.some(branch => branch.get_ctx_name);
|
||||
const select_block_ctx = need_select_block_ctx ? block.get_unique_name('select_block_ctx') : null;
|
||||
const if_ctx = select_block_ctx ? x`${select_block_ctx}(#ctx, ${current_block_type_index})` : x`#ctx`;
|
||||
|
||||
const if_current_block_type_index = has_else
|
||||
? nodes => nodes
|
||||
: nodes => b`if (~${current_block_type_index}) { ${nodes} }`;
|
||||
|
||||
block.add_variable(current_block_type_index);
|
||||
block.add_variable(name);
|
||||
|
||||
block.chunks.init.push(b`
|
||||
const ${if_block_creators} = [
|
||||
${this.branches.map(branch => branch.block.name)}
|
||||
];
|
||||
|
||||
const ${if_blocks} = [];
|
||||
|
||||
${this.needs_update
|
||||
? b`
|
||||
function ${select_block_type}(#ctx, #dirty) {
|
||||
${this.branches.map(({ dependencies, condition, snippet }) => {
|
||||
return b`${snippet && dependencies.length > 0 ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = null;` : null}`;
|
||||
})}
|
||||
${this.branches.map(({ condition, snippet }, i) => condition
|
||||
? b`
|
||||
${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`}
|
||||
if (${condition}) return ${i};`
|
||||
: b`return ${i};`)}
|
||||
${!has_else && b`return -1;`}
|
||||
}
|
||||
`
|
||||
: b`
|
||||
function ${select_block_type}(#ctx, #dirty) {
|
||||
${this.branches.map(({ condition, snippet }, i) => condition
|
||||
? b`if (${snippet || condition}) return ${i};`
|
||||
: b`return ${i};`)}
|
||||
${!has_else && b`return -1;`}
|
||||
}
|
||||
`}
|
||||
`);
|
||||
|
||||
if (need_select_block_ctx) {
|
||||
// if all branches needs create a context
|
||||
if (this.branches.every(branch => branch.get_ctx_name)) {
|
||||
block.chunks.init.push(b`
|
||||
function ${select_block_ctx}(#ctx, #index) {
|
||||
${this.branches.map(({ condition, get_ctx_name }, i) => {
|
||||
return condition
|
||||
? b`if (#index === ${i}) return ${get_ctx_name}(#ctx);`
|
||||
: b`return ${get_ctx_name}(#ctx);`;
|
||||
}).filter(Boolean)}
|
||||
}
|
||||
`);
|
||||
} else {
|
||||
// when not all branches need to create a new context,
|
||||
// this code is simpler
|
||||
block.chunks.init.push(b`
|
||||
function ${select_block_ctx}(#ctx, #index) {
|
||||
${this.branches.map(({ get_ctx_name }, i) => {
|
||||
return get_ctx_name
|
||||
? b`if (#index === ${i}) return ${get_ctx_name}(#ctx);`
|
||||
: null;
|
||||
}).filter(Boolean)}
|
||||
return #ctx;
|
||||
}
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_else) {
|
||||
block.chunks.init.push(b`
|
||||
${current_block_type_index} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()});
|
||||
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](${if_ctx});
|
||||
`);
|
||||
} else {
|
||||
block.chunks.init.push(b`
|
||||
if (~(${current_block_type_index} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()}))) {
|
||||
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](${if_ctx});
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const initial_mount_node = parent_node || '#target';
|
||||
const anchor_node = parent_node ? 'null' : '#anchor';
|
||||
|
||||
block.chunks.mount.push(
|
||||
if_current_block_type_index(
|
||||
b`${if_blocks}[${current_block_type_index}].m(${initial_mount_node}, ${anchor_node});`
|
||||
)
|
||||
);
|
||||
|
||||
if (this.needs_update) {
|
||||
const update_mount_node = this.get_update_mount_node(anchor);
|
||||
|
||||
const destroy_old_block = b`
|
||||
@group_outros();
|
||||
@transition_out(${if_blocks}[${previous_block_index}], 1, 1, () => {
|
||||
${if_blocks}[${previous_block_index}] = null;
|
||||
});
|
||||
@check_outros();
|
||||
`;
|
||||
|
||||
const create_new_block = b`
|
||||
${name} = ${if_blocks}[${current_block_type_index}];
|
||||
if (!${name}) {
|
||||
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](${if_ctx});
|
||||
${name}.c();
|
||||
} else {
|
||||
${dynamic && b`${name}.p(${if_ctx}, #dirty);`}
|
||||
}
|
||||
${has_transitions && b`@transition_in(${name}, 1);`}
|
||||
${name}.m(${update_mount_node}, ${anchor});
|
||||
`;
|
||||
|
||||
const change_block = has_else
|
||||
? b`
|
||||
${destroy_old_block}
|
||||
|
||||
${create_new_block}
|
||||
`
|
||||
: b`
|
||||
if (${name}) {
|
||||
${destroy_old_block}
|
||||
}
|
||||
|
||||
if (~${current_block_type_index}) {
|
||||
${create_new_block}
|
||||
} else {
|
||||
${name} = null;
|
||||
}
|
||||
`;
|
||||
|
||||
block.chunks.update.push(b`
|
||||
let ${previous_block_index} = ${current_block_type_index};
|
||||
${current_block_type_index} = ${select_block_type}(#ctx, #dirty);
|
||||
`);
|
||||
|
||||
if (dynamic) {
|
||||
block.chunks.update.push(b`
|
||||
if (${current_block_type_index} === ${previous_block_index}) {
|
||||
${if_current_block_type_index(b`${if_blocks}[${current_block_type_index}].p(${if_ctx}, #dirty);`)}
|
||||
} else {
|
||||
${change_block}
|
||||
}
|
||||
`);
|
||||
} else {
|
||||
block.chunks.update.push(b`
|
||||
if (${current_block_type_index} !== ${previous_block_index}) {
|
||||
${change_block}
|
||||
}
|
||||
`);
|
||||
}
|
||||
} else if (dynamic) {
|
||||
if (if_exists_condition) {
|
||||
block.chunks.update.push(b`if (${if_exists_condition}) ${name}.p(${if_ctx}, #dirty);`);
|
||||
} else {
|
||||
block.chunks.update.push(b`${name}.p(${if_ctx}, #dirty);`);
|
||||
}
|
||||
}
|
||||
|
||||
block.chunks.destroy.push(
|
||||
if_current_block_type_index(b`${if_blocks}[${current_block_type_index}].d(${detaching});`)
|
||||
);
|
||||
}
|
||||
|
||||
render_simple(
|
||||
block: Block,
|
||||
parent_node: Identifier,
|
||||
_parent_nodes: Identifier,
|
||||
dynamic,
|
||||
{ name, anchor, if_exists_condition, has_transitions },
|
||||
detaching
|
||||
) {
|
||||
const branch = this.branches[0];
|
||||
const if_ctx = branch.get_ctx_name ? x`${branch.get_ctx_name}(#ctx)` : x`#ctx`;
|
||||
|
||||
if (branch.snippet) block.add_variable(branch.condition, branch.snippet);
|
||||
|
||||
block.chunks.init.push(b`
|
||||
let ${name} = ${branch.condition} && ${branch.block.name}(${if_ctx});
|
||||
`);
|
||||
|
||||
const initial_mount_node = parent_node || '#target';
|
||||
const anchor_node = parent_node ? 'null' : '#anchor';
|
||||
|
||||
block.chunks.mount.push(
|
||||
b`if (${name}) ${name}.m(${initial_mount_node}, ${anchor_node});`
|
||||
);
|
||||
|
||||
if (branch.dependencies.length > 0) {
|
||||
const update_mount_node = this.get_update_mount_node(anchor);
|
||||
|
||||
const enter = b`
|
||||
if (${name}) {
|
||||
${dynamic && b`${name}.p(${if_ctx}, #dirty);`}
|
||||
${has_transitions &&
|
||||
b`if (${block.renderer.dirty(branch.dependencies)}) {
|
||||
@transition_in(${name}, 1);
|
||||
}`
|
||||
}
|
||||
} else {
|
||||
${name} = ${branch.block.name}(${if_ctx});
|
||||
${name}.c();
|
||||
${has_transitions && b`@transition_in(${name}, 1);`}
|
||||
${name}.m(${update_mount_node}, ${anchor});
|
||||
}
|
||||
`;
|
||||
|
||||
if (branch.snippet) {
|
||||
block.chunks.update.push(b`if (${block.renderer.dirty(branch.dependencies)}) ${branch.condition} = ${branch.snippet}`);
|
||||
}
|
||||
|
||||
// no `p()` here — we don't want to update outroing nodes,
|
||||
// as that will typically result in glitching
|
||||
if (branch.block.has_outro_method) {
|
||||
block.chunks.update.push(b`
|
||||
if (${branch.condition}) {
|
||||
${enter}
|
||||
} else if (${name}) {
|
||||
@group_outros();
|
||||
@transition_out(${name}, 1, 1, () => {
|
||||
${name} = null;
|
||||
});
|
||||
@check_outros();
|
||||
}
|
||||
`);
|
||||
} else {
|
||||
block.chunks.update.push(b`
|
||||
if (${branch.condition}) {
|
||||
${enter}
|
||||
} else if (${name}) {
|
||||
${name}.d(1);
|
||||
${name} = null;
|
||||
}
|
||||
`);
|
||||
}
|
||||
} else if (dynamic) {
|
||||
block.chunks.update.push(b`
|
||||
if (${branch.condition}) ${name}.p(${if_ctx}, #dirty);
|
||||
`);
|
||||
}
|
||||
|
||||
if (if_exists_condition) {
|
||||
block.chunks.destroy.push(b`
|
||||
if (${if_exists_condition}) ${name}.d(${detaching});
|
||||
`);
|
||||
} else {
|
||||
block.chunks.destroy.push(b`
|
||||
${name}.d(${detaching});
|
||||
`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
{#switch foo case bar}<p>bar</p>{:case foo}<p>foo</p>{/switch}
|
||||
@ -0,0 +1,109 @@
|
||||
{
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 62,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 62,
|
||||
"type": "SwitchBlock",
|
||||
"discriminant": {
|
||||
"type": "Identifier",
|
||||
"start": 9,
|
||||
"end": 12,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 9
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 12
|
||||
}
|
||||
},
|
||||
"name": "foo"
|
||||
},
|
||||
"cases": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 32,
|
||||
"type": "CaseBlock",
|
||||
"test": {
|
||||
"type": "Identifier",
|
||||
"start": 18,
|
||||
"end": 21,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 18
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 21
|
||||
}
|
||||
},
|
||||
"name": "bar"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"start": 22,
|
||||
"end": 32,
|
||||
"type": "Element",
|
||||
"name": "p",
|
||||
"attributes": [],
|
||||
"children": [
|
||||
{
|
||||
"start": 25,
|
||||
"end": 28,
|
||||
"type": "Text",
|
||||
"raw": "bar",
|
||||
"data": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"start": 32,
|
||||
"end": 53,
|
||||
"type": "CaseBlock",
|
||||
"test": {
|
||||
"type": "Identifier",
|
||||
"start": 39,
|
||||
"end": 42,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 39
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 42
|
||||
}
|
||||
},
|
||||
"name": "foo"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"start": 43,
|
||||
"end": 53,
|
||||
"type": "Element",
|
||||
"name": "p",
|
||||
"attributes": [],
|
||||
"children": [
|
||||
{
|
||||
"start": 46,
|
||||
"end": 49,
|
||||
"type": "Text",
|
||||
"raw": "foo",
|
||||
"data": "foo"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
{#switch foo}<p>default</p>{:case bar}<p>bar</p>{/switch}
|
||||
@ -0,0 +1,94 @@
|
||||
{
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 57,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 57,
|
||||
"type": "SwitchBlock",
|
||||
"discriminant": {
|
||||
"type": "Identifier",
|
||||
"start": 9,
|
||||
"end": 12,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 9
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 12
|
||||
}
|
||||
},
|
||||
"name": "foo"
|
||||
},
|
||||
"cases": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 27,
|
||||
"type": "CaseBlock",
|
||||
"isdefault": true,
|
||||
"children": [
|
||||
{
|
||||
"start": 13,
|
||||
"end": 27,
|
||||
"type": "Element",
|
||||
"name": "p",
|
||||
"attributes": [],
|
||||
"children": [
|
||||
{
|
||||
"start": 16,
|
||||
"end": 23,
|
||||
"type": "Text",
|
||||
"raw": "default",
|
||||
"data": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"start": 27,
|
||||
"end": 48,
|
||||
"type": "CaseBlock",
|
||||
"test": {
|
||||
"type": "Identifier",
|
||||
"start": 34,
|
||||
"end": 37,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 34
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 37
|
||||
}
|
||||
},
|
||||
"name": "bar"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"start": 38,
|
||||
"end": 48,
|
||||
"type": "Element",
|
||||
"name": "p",
|
||||
"attributes": [],
|
||||
"children": [
|
||||
{
|
||||
"start": 41,
|
||||
"end": 44,
|
||||
"type": "Text",
|
||||
"raw": "bar",
|
||||
"data": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
export default {
|
||||
props: {
|
||||
thing: 'foo'
|
||||
},
|
||||
|
||||
html: '<p>i am foo</p>',
|
||||
|
||||
test({ assert, component, target }) {
|
||||
assert.htmlEqual( target.innerHTML, '<p>i am foo</p>' );
|
||||
component.thing = 'bar';
|
||||
assert.htmlEqual( target.innerHTML, '<p>i am bar</p>' );
|
||||
component.thing = 'no-match';
|
||||
assert.htmlEqual( target.innerHTML, '<p>i am default</p>' );
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
export let thing;
|
||||
|
||||
let a = "foo"
|
||||
let b = "bar"
|
||||
</script>
|
||||
|
||||
{#switch thing case a}
|
||||
<p>i am foo</p>
|
||||
{:case b}
|
||||
<p>i am bar</p>
|
||||
{/switch}
|
||||
@ -0,0 +1,15 @@
|
||||
export default {
|
||||
props: {
|
||||
thing: 'foo'
|
||||
},
|
||||
|
||||
html: '<p>i am foo</p>',
|
||||
|
||||
test({ assert, component, target }) {
|
||||
assert.htmlEqual( target.innerHTML, '<p>i am foo</p>' );
|
||||
component.thing = 'bar';
|
||||
assert.htmlEqual( target.innerHTML, '<p>i am bar</p>' );
|
||||
component.thing = 'no-match';
|
||||
assert.htmlEqual( target.innerHTML, '<p>i am default</p>' );
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,14 @@
|
||||
<script>
|
||||
export let thing;
|
||||
|
||||
let a = "foo"
|
||||
let b = "bar"
|
||||
</script>
|
||||
|
||||
{#switch thing}
|
||||
<p>i am default</p>
|
||||
{:case a}
|
||||
<p>i am foo</p>
|
||||
{:case b}
|
||||
<p>i am bar</p>
|
||||
{/switch}
|
||||
Loading…
Reference in new issue