[feat] support const tag for if block ()

pull/7505/head
Tan Li Hau 3 years ago committed by GitHub
parent 61d1467c25
commit a2de3894c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -465,16 +465,16 @@ The `{@const ...}` tag defines a local constant.
```sv ```sv
<script> <script>
export let boxes; export let boxes;
</script> </script>
{#each boxes as box} {#each boxes as box}
{@const area = box.width * box.height} {@const area = box.width * box.height}
{box.width} * {box.height} = {area} {box.width} * {box.height} = {area}
{/each} {/each}
``` ```
`{@const}` is only allowed as direct child of `{#each}`, `{:then}`, `{:catch}`, `<Component />` or `<svelte:fragment />`. `{@const}` is only allowed as direct child of `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<Component />` or `<svelte:fragment />`.
### Element directives ### Element directives

@ -260,7 +260,7 @@ export default {
}, },
invalid_const_placement: { invalid_const_placement: {
code: 'invalid-const-placement', code: 'invalid-const-placement',
message: '{@const} must be the immediate child of {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>' message: '{@const} must be the immediate child of {#if}, {:else if}, {:else}, {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>'
}, },
invalid_const_declaration: (name: string) => ({ invalid_const_declaration: (name: string) => ({
code: 'invalid-const-declaration', code: 'invalid-const-declaration',

@ -11,7 +11,7 @@ import is_reference, { NodeWithPropertyDefinition } from 'is-reference';
import get_object from '../utils/get_object'; import get_object from '../utils/get_object';
import compiler_errors from '../compiler_errors'; import compiler_errors from '../compiler_errors';
const allowed_parents = new Set(['EachBlock', 'CatchBlock', 'ThenBlock', 'InlineComponent', 'SlotTemplate']); const allowed_parents = new Set(['EachBlock', 'CatchBlock', 'ThenBlock', 'InlineComponent', 'SlotTemplate', 'IfBlock', 'ElseBlock']);
export default class ConstTag extends Node { export default class ConstTag extends Node {
type: 'ConstTag'; type: 'ConstTag';

@ -1,16 +1,21 @@
import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock'; import AbstractBlock from './shared/AbstractBlock';
import Component from '../Component'; import Component from '../Component';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import { TemplateNode } from '../../interfaces'; import { TemplateNode } from '../../interfaces';
import Node from './shared/Node'; import Node from './shared/Node';
import ConstTag from './ConstTag';
import get_const_tags from './shared/get_const_tags';
export default class ElseBlock extends AbstractBlock { export default class ElseBlock extends AbstractBlock {
type: 'ElseBlock'; type: 'ElseBlock';
scope: TemplateScope;
const_tags: ConstTag[];
constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) { constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info); super(component, parent, scope, info);
this.children = map_children(component, this, scope, info.children);
this.scope = scope.child();
([this.const_tags, this.children] = get_const_tags(info.children, component, this, this));
this.warn_if_empty_block(); this.warn_if_empty_block();
} }

@ -1,22 +1,26 @@
import ElseBlock from './ElseBlock'; import ElseBlock from './ElseBlock';
import Expression from './shared/Expression'; import Expression from './shared/Expression';
import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock'; import AbstractBlock from './shared/AbstractBlock';
import Component from '../Component'; import Component from '../Component';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import { TemplateNode } from '../../interfaces'; import { TemplateNode } from '../../interfaces';
import Node from './shared/Node'; import Node from './shared/Node';
import ConstTag from './ConstTag';
import get_const_tags from './shared/get_const_tags';
export default class IfBlock extends AbstractBlock { export default class IfBlock extends AbstractBlock {
type: 'IfBlock'; type: 'IfBlock';
expression: Expression; expression: Expression;
else: ElseBlock; else: ElseBlock;
scope: TemplateScope;
const_tags: ConstTag[];
constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) { constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info); super(component, parent, scope, info);
this.scope = scope.child();
this.expression = new Expression(component, this, scope, info.expression); this.expression = new Expression(component, this, this.scope, info.expression);
this.children = map_children(component, this, scope, info.children); ([this.const_tags, this.children] = get_const_tags(info.children, component, this, this));
this.else = info.else this.else = info.else
? new ElseBlock(component, this, scope, info.else) ? new ElseBlock(component, this, scope, info.else)

@ -72,6 +72,8 @@ export type INode = Action
| Window; | Window;
export type INodeAllowConstTag = export type INodeAllowConstTag =
| IfBlock
| ElseBlock
| EachBlock | EachBlock
| CatchBlock | CatchBlock
| ThenBlock | ThenBlock

@ -27,6 +27,7 @@ export class ElseBlockWrapper extends Wrapper {
next_sibling: Wrapper next_sibling: Wrapper
) { ) {
super(renderer, block, parent, node); super(renderer, block, parent, node);
add_const_tags_context(renderer, this.node.const_tags);
this.block = block.child({ this.block = block.child({
comment: create_debugging_comment(node, this.renderer.component), comment: create_debugging_comment(node, this.renderer.component),
@ -257,6 +258,18 @@ export default class EachBlockWrapper extends Wrapper {
} }
if (this.else) { if (this.else) {
let else_ctx = x`#ctx`;
if (this.else.node.const_tags.length > 0) {
const get_ctx_name = this.renderer.component.get_unique_name('get_else_ctx');
this.renderer.blocks.push(b`
function ${get_ctx_name}(#ctx) {
const child_ctx = #ctx.slice();
${add_const_tags(block, this.else.node.const_tags, 'child_ctx')}
return child_ctx;
}
`);
else_ctx = x`${get_ctx_name}(#ctx)`;
}
const each_block_else = component.get_unique_name(`${this.var.name}_else`); const each_block_else = component.get_unique_name(`${this.var.name}_else`);
block.chunks.init.push(b`let ${each_block_else} = null;`); block.chunks.init.push(b`let ${each_block_else} = null;`);
@ -264,7 +277,7 @@ export default class EachBlockWrapper extends Wrapper {
// TODO neaten this up... will end up with an empty line in the block // TODO neaten this up... will end up with an empty line in the block
block.chunks.init.push(b` block.chunks.init.push(b`
if (!${this.vars.data_length}) { if (!${this.vars.data_length}) {
${each_block_else} = ${this.else.block.name}(#ctx); ${each_block_else} = ${this.else.block.name}(${else_ctx});
} }
`); `);
@ -304,9 +317,9 @@ export default class EachBlockWrapper extends Wrapper {
if (this.else.block.has_update_method) { if (this.else.block.has_update_method) {
this.updates.push(b` this.updates.push(b`
if (!${this.vars.data_length} && ${each_block_else}) { if (!${this.vars.data_length} && ${each_block_else}) {
${each_block_else}.p(#ctx, #dirty); ${each_block_else}.p(${else_ctx}, #dirty);
} else if (!${this.vars.data_length}) { } else if (!${this.vars.data_length}) {
${each_block_else} = ${this.else.block.name}(#ctx); ${each_block_else} = ${this.else.block.name}(${else_ctx});
${each_block_else}.c(); ${each_block_else}.c();
${has_transitions && b`@transition_in(${each_block_else}, 1);`} ${has_transitions && b`@transition_in(${each_block_else}, 1);`}
${each_block_else}.m(${update_mount_node}, ${update_anchor_node}); ${each_block_else}.m(${update_mount_node}, ${update_anchor_node});
@ -321,7 +334,7 @@ export default class EachBlockWrapper extends Wrapper {
${destroy_block_else}; ${destroy_block_else};
} }
} else if (!${each_block_else}) { } else if (!${each_block_else}) {
${each_block_else} = ${this.else.block.name}(#ctx); ${each_block_else} = ${this.else.block.name}(${else_ctx});
${each_block_else}.c(); ${each_block_else}.c();
${has_transitions && b`@transition_in(${each_block_else}, 1);`} ${has_transitions && b`@transition_in(${each_block_else}, 1);`}
${each_block_else}.m(${update_mount_node}, ${update_anchor_node}); ${each_block_else}.m(${update_mount_node}, ${update_anchor_node});

@ -11,6 +11,7 @@ import { walk } from 'estree-walker';
import { is_head } from './shared/is_head'; import { is_head } from './shared/is_head';
import { Identifier, Node } from 'estree'; import { Identifier, Node } from 'estree';
import { push_array } from '../../../utils/push_array'; import { push_array } from '../../../utils/push_array';
import { add_const_tags, add_const_tags_context } from './shared/add_const_tags';
function is_else_if(node: ElseBlock) { function is_else_if(node: ElseBlock) {
return ( return (
@ -25,8 +26,10 @@ class IfBlockBranch extends Wrapper {
condition?: any; condition?: any;
snippet?: Node; snippet?: Node;
is_dynamic: boolean; is_dynamic: boolean;
node: IfBlock | ElseBlock;
var = null; var = null;
get_ctx_name: Node | undefined;
constructor( constructor(
renderer: Renderer, renderer: Renderer,
@ -63,6 +66,8 @@ class IfBlockBranch extends Wrapper {
} }
} }
add_const_tags_context(renderer, this.node.const_tags);
this.block = block.child({ this.block = block.child({
comment: create_debugging_comment(node, parent.renderer.component), comment: create_debugging_comment(node, parent.renderer.component),
name: parent.renderer.component.get_unique_name( name: parent.renderer.component.get_unique_name(
@ -74,6 +79,10 @@ class IfBlockBranch extends Wrapper {
this.fragment = new FragmentWrapper(renderer, this.block, node.children, parent, strip_whitespace, next_sibling); this.fragment = new FragmentWrapper(renderer, this.block, node.children, parent, strip_whitespace, next_sibling);
this.is_dynamic = this.block.dependencies.size > 0; this.is_dynamic = this.block.dependencies.size > 0;
if (node.const_tags.length > 0) {
this.get_ctx_name = parent.renderer.component.get_unique_name(is_else ? 'get_else_ctx' : 'get_if_ctx');
}
} }
} }
@ -190,6 +199,18 @@ export default class IfBlockWrapper extends Wrapper {
const has_outros = this.branches[0].block.has_outro_method; const has_outros = this.branches[0].block.has_outro_method;
const has_transitions = has_intros || has_outros; 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 vars = { name, anchor, if_exists_condition, has_else, has_transitions };
const detaching = parent_node && !is_head(parent_node) ? null : 'detaching'; const detaching = parent_node && !is_head(parent_node) ? null : 'detaching';
@ -260,9 +281,13 @@ export default class IfBlockWrapper extends Wrapper {
) { ) {
const select_block_type = this.renderer.component.get_unique_name('select_block_type'); 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 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 const get_block = has_else
? x`${current_block_type}(#ctx)` ? x`${current_block_type}(${if_ctx})`
: x`${current_block_type} && ${current_block_type}(#ctx)`; : x`${current_block_type} && ${current_block_type}(${if_ctx})`;
if (this.needs_update) { if (this.needs_update) {
block.chunks.init.push(b` block.chunks.init.push(b`
@ -271,10 +296,10 @@ export default class IfBlockWrapper extends Wrapper {
return b`${snippet && dependencies.length > 0 ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = null;` : null}`; return b`${snippet && dependencies.length > 0 ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = null;` : null}`;
})} })}
${this.branches.map(({ condition, snippet, block }) => condition ${this.branches.map(({ condition, snippet, block }) => condition
? b` ? b`
${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`} ${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`}
if (${condition}) return ${block.name};` if (${condition}) return ${block.name};`
: b`return ${block.name};` : b`return ${block.name};`
)} )}
} }
`); `);
@ -282,12 +307,40 @@ export default class IfBlockWrapper extends Wrapper {
block.chunks.init.push(b` block.chunks.init.push(b`
function ${select_block_type}(#ctx, #dirty) { function ${select_block_type}(#ctx, #dirty) {
${this.branches.map(({ condition, snippet, block }) => condition ${this.branches.map(({ condition, snippet, block }) => condition
? b`if (${snippet || condition}) return ${block.name};` ? b`if (${snippet || condition}) return ${block.name};`
: b`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` block.chunks.init.push(b`
let ${current_block_type} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()}); let ${current_block_type} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()});
let ${name} = ${get_block}; let ${name} = ${get_block};
@ -322,7 +375,7 @@ export default class IfBlockWrapper extends Wrapper {
if (dynamic) { if (dynamic) {
block.chunks.update.push(b` block.chunks.update.push(b`
if (${current_block_type} === (${current_block_type} = ${select_block_type}(#ctx, #dirty)) && ${name}) { if (${current_block_type} === (${current_block_type} = ${select_block_type}(#ctx, #dirty)) && ${name}) {
${name}.p(#ctx, #dirty); ${name}.p(${if_ctx}, #dirty);
} else { } else {
${change_block} ${change_block}
} }
@ -336,9 +389,9 @@ export default class IfBlockWrapper extends Wrapper {
} }
} else if (dynamic) { } else if (dynamic) {
if (if_exists_condition) { if (if_exists_condition) {
block.chunks.update.push(b`if (${if_exists_condition}) ${name}.p(#ctx, #dirty);`); block.chunks.update.push(b`if (${if_exists_condition}) ${name}.p(${if_ctx}, #dirty);`);
} else { } else {
block.chunks.update.push(b`${name}.p(#ctx, #dirty);`); block.chunks.update.push(b`${name}.p(${if_ctx}, #dirty);`);
} }
} }
@ -370,6 +423,9 @@ export default class IfBlockWrapper extends Wrapper {
const previous_block_index = block.get_unique_name('previous_block_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_block_creators = block.get_unique_name('if_block_creators');
const if_blocks = block.get_unique_name('if_blocks'); 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 const if_current_block_type_index = has_else
? nodes => nodes ? nodes => nodes
@ -392,32 +448,60 @@ export default class IfBlockWrapper extends Wrapper {
return b`${snippet && dependencies.length > 0 ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = null;` : null}`; return b`${snippet && dependencies.length > 0 ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = null;` : null}`;
})} })}
${this.branches.map(({ condition, snippet }, i) => condition ${this.branches.map(({ condition, snippet }, i) => condition
? b` ? b`
${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`} ${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`}
if (${condition}) return ${i};` if (${condition}) return ${i};`
: b`return ${i};`)} : b`return ${i};`)}
${!has_else && b`return -1;`} ${!has_else && b`return -1;`}
} }
` `
: b` : b`
function ${select_block_type}(#ctx, #dirty) { function ${select_block_type}(#ctx, #dirty) {
${this.branches.map(({ condition, snippet }, i) => condition ${this.branches.map(({ condition, snippet }, i) => condition
? b`if (${snippet || condition}) return ${i};` ? b`if (${snippet || condition}) return ${i};`
: b`return ${i};`)} : b`return ${i};`)}
${!has_else && b`return -1;`} ${!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) { if (has_else) {
block.chunks.init.push(b` block.chunks.init.push(b`
${current_block_type_index} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()}); ${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}](#ctx); ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](${if_ctx});
`); `);
} else { } else {
block.chunks.init.push(b` block.chunks.init.push(b`
if (~(${current_block_type_index} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()}))) { 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}](#ctx); ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](${if_ctx});
} }
`); `);
} }
@ -445,10 +529,10 @@ export default class IfBlockWrapper extends Wrapper {
const create_new_block = b` const create_new_block = b`
${name} = ${if_blocks}[${current_block_type_index}]; ${name} = ${if_blocks}[${current_block_type_index}];
if (!${name}) { if (!${name}) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#ctx); ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](${if_ctx});
${name}.c(); ${name}.c();
} else { } else {
${dynamic && b`${name}.p(#ctx, #dirty);`} ${dynamic && b`${name}.p(${if_ctx}, #dirty);`}
} }
${has_transitions && b`@transition_in(${name}, 1);`} ${has_transitions && b`@transition_in(${name}, 1);`}
${name}.m(${update_mount_node}, ${anchor}); ${name}.m(${update_mount_node}, ${anchor});
@ -480,7 +564,7 @@ export default class IfBlockWrapper extends Wrapper {
if (dynamic) { if (dynamic) {
block.chunks.update.push(b` block.chunks.update.push(b`
if (${current_block_type_index} === ${previous_block_index}) { if (${current_block_type_index} === ${previous_block_index}) {
${if_current_block_type_index(b`${if_blocks}[${current_block_type_index}].p(#ctx, #dirty);`)} ${if_current_block_type_index(b`${if_blocks}[${current_block_type_index}].p(${if_ctx}, #dirty);`)}
} else { } else {
${change_block} ${change_block}
} }
@ -494,9 +578,9 @@ export default class IfBlockWrapper extends Wrapper {
} }
} else if (dynamic) { } else if (dynamic) {
if (if_exists_condition) { if (if_exists_condition) {
block.chunks.update.push(b`if (${if_exists_condition}) ${name}.p(#ctx, #dirty);`); block.chunks.update.push(b`if (${if_exists_condition}) ${name}.p(${if_ctx}, #dirty);`);
} else { } else {
block.chunks.update.push(b`${name}.p(#ctx, #dirty);`); block.chunks.update.push(b`${name}.p(${if_ctx}, #dirty);`);
} }
} }
@ -514,11 +598,12 @@ export default class IfBlockWrapper extends Wrapper {
detaching detaching
) { ) {
const branch = this.branches[0]; 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); if (branch.snippet) block.add_variable(branch.condition, branch.snippet);
block.chunks.init.push(b` block.chunks.init.push(b`
let ${name} = ${branch.condition} && ${branch.block.name}(#ctx); let ${name} = ${branch.condition} && ${branch.block.name}(${if_ctx});
`); `);
const initial_mount_node = parent_node || '#target'; const initial_mount_node = parent_node || '#target';
@ -533,15 +618,14 @@ export default class IfBlockWrapper extends Wrapper {
const enter = b` const enter = b`
if (${name}) { if (${name}) {
${dynamic && b`${name}.p(#ctx, #dirty);`} ${dynamic && b`${name}.p(${if_ctx}, #dirty);`}
${ ${has_transitions &&
has_transitions &&
b`if (${block.renderer.dirty(branch.dependencies)}) { b`if (${block.renderer.dirty(branch.dependencies)}) {
@transition_in(${name}, 1); @transition_in(${name}, 1);
}` }`
} }
} else { } else {
${name} = ${branch.block.name}(#ctx); ${name} = ${branch.block.name}(${if_ctx});
${name}.c(); ${name}.c();
${has_transitions && b`@transition_in(${name}, 1);`} ${has_transitions && b`@transition_in(${name}, 1);`}
${name}.m(${update_mount_node}, ${anchor}); ${name}.m(${update_mount_node}, ${anchor});
@ -578,7 +662,7 @@ export default class IfBlockWrapper extends Wrapper {
} }
} else if (dynamic) { } else if (dynamic) {
block.chunks.update.push(b` block.chunks.update.push(b`
if (${branch.condition}) ${name}.p(#ctx, #dirty); if (${branch.condition}) ${name}.p(${if_ctx}, #dirty);
`); `);
} }

@ -2,6 +2,7 @@ import Renderer, { RenderOptions } from '../Renderer';
import EachBlock from '../../nodes/EachBlock'; import EachBlock from '../../nodes/EachBlock';
import { x } from 'code-red'; import { x } from 'code-red';
import { get_const_tags } from './shared/get_const_tags'; import { get_const_tags } from './shared/get_const_tags';
import { Node } from 'estree';
export default function(node: EachBlock, renderer: Renderer, options: RenderOptions) { export default function(node: EachBlock, renderer: Renderer, options: RenderOptions) {
const args = [node.context_node]; const args = [node.context_node];
@ -16,7 +17,8 @@ export default function(node: EachBlock, renderer: Renderer, options: RenderOpti
if (node.else) { if (node.else) {
renderer.push(); renderer.push();
renderer.render(node.else.children, options); renderer.render(node.else.children, options);
const alternate = renderer.pop(); let alternate: Node = renderer.pop();
if (node.else.const_tags.length > 0) alternate = x`(() => { ${get_const_tags(node.else.const_tags)}; return ${alternate} })()`;
renderer.add_expression(x`${node.expression.node}.length ? ${consequent} : ${alternate}`); renderer.add_expression(x`${node.expression.node}.length ? ${consequent} : ${alternate}`);
} else { } else {

@ -1,17 +1,21 @@
import IfBlock from '../../nodes/IfBlock'; import IfBlock from '../../nodes/IfBlock';
import Renderer, { RenderOptions } from '../Renderer'; import Renderer, { RenderOptions } from '../Renderer';
import { x } from 'code-red'; import { x } from 'code-red';
import { get_const_tags } from './shared/get_const_tags';
import { Node } from 'estree';
export default function(node: IfBlock, renderer: Renderer, options: RenderOptions) { export default function (node: IfBlock, renderer: Renderer, options: RenderOptions) {
const condition = node.expression.node; const condition = node.expression.node;
renderer.push(); renderer.push();
renderer.render(node.children, options); renderer.render(node.children, options);
const consequent = renderer.pop(); let consequent: Node = renderer.pop();
if (node.const_tags.length > 0) consequent = x`(() => { ${get_const_tags(node.const_tags)}; return ${consequent} })()`;
renderer.push(); renderer.push();
if (node.else) renderer.render(node.else.children, options); if (node.else) renderer.render(node.else.children, options);
const alternate = renderer.pop(); let alternate: Node = renderer.pop();
if (node.else && node.else.const_tags.length > 0) alternate = x`(() => { ${get_const_tags(node.else.const_tags)}; return ${alternate} })()`;
renderer.add_expression(x`${condition} ? ${consequent} : ${alternate}`); renderer.add_expression(x`${condition} ? ${consequent} : ${alternate}`);
} }

@ -0,0 +1,26 @@
export default {
html: `
<div>12 120 70, 30+4=34</div>
<div>35 350 120, 50+7=57</div>
<div>48 480 140, 60+8=68</div>
`,
async test({ component, target, assert }) {
component.boxes = [];
assert.htmlEqual(target.innerHTML, `
<div>10 * 2 = 20</div>
`);
component.constant = 35;
assert.htmlEqual(target.innerHTML, `
<div>35 * 2 = 70</div>
`);
component.boxes = [
{width: 3, height: 4}
];
assert.htmlEqual(target.innerHTML, `
<div>12 420 245, 105+4=109</div>
`);
}
};

@ -0,0 +1,22 @@
<script>
export let boxes = [
{width: 3, height: 4},
{width: 5, height: 7},
{width: 6, height: 8},
];
export let constant = 10;
function calculate(width, height, constant) {
return { area: width * height, volume: width * height * constant };
}
</script>
{#each boxes as box}
{@const {area, volume} = calculate(box.width, box.height, constant)}
{@const perimeter = (box.width + box.height) * constant}
{@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
<div>{area} {volume} {perimeter}, {width}+{height}={sum}</div>
{:else}
{@const double = constant + constant}
<div>{constant} * 2 = {double}</div>
{/each}

@ -0,0 +1,56 @@
export default {
html: `
<div>20 x 40</div>
<div>20 x 40</div>
`,
props: {
boxes: [{ width: 20, height: 40 }]
},
async test({ component, target, assert }) {
component.boxes = [{ width: 40, height: 70 }];
assert.htmlEqual(
target.innerHTML,
`
<div>40 x 70</div>
<div>40 x 70</div>
`
);
component.boxes = [];
assert.htmlEqual(target.innerHTML, '');
component.boxes = [
{ width: 20, height: 40 },
{ width: 30, height: 50 }
];
assert.htmlEqual(
target.innerHTML,
`
<div>20 x 40</div>
<div>30 x 50</div>
<div>20 x 40</div>
<div>30 x 50</div>
`
);
component.boxes = [
{ width: 80, height: 70 },
{ width: 90, height: 60 }
];
assert.htmlEqual(
target.innerHTML,
`
<div>80 x 70</div>
<div>90 x 60</div>
<div>80 x 70</div>
<div>90 x 60</div>
`
);
component.boxes = [];
assert.htmlEqual(target.innerHTML, '');
}
};

@ -0,0 +1,24 @@
<script>
export let boxes;
</script>
{#if boxes.length > 1}
{@const box1 = boxes[0]}
{@const box2 = boxes[1]}
{@const { width, height } = box1}
<div>{width} x {height}</div>
<div>{box2.width} x {box2.height}</div>
{:else if boxes.length > 0}
{@const box = boxes[0]}
{@const { width, height } = box}
<div>{width} x {height}</div>
{/if}
{#if boxes.length > 1}
<div>{boxes[0].width} x {boxes[0].height}</div>
<div>{boxes[1].width} x {boxes[1].height}</div>
{:else if boxes.length > 0}
{@const box = boxes[0]}
{@const { width, height } = box}
<div>{width} x {height}</div>
{/if}

@ -0,0 +1,57 @@
export default {
html: `
<div>20 x 40</div>
<div>20 x 40</div>
`,
props: {
boxes: [{ width: 20, height: 40 }]
},
async test({ component, target, assert, raf }) {
component.boxes = [{ width: 40, height: 70 }];
assert.htmlEqual(
target.innerHTML,
`
<div>40 x 70</div>
<div>40 x 70</div>
`
);
component.boxes = [];
raf.tick(0);
assert.htmlEqual(target.innerHTML, '');
component.boxes = [
{ width: 20, height: 40 },
{ width: 30, height: 50 }
];
assert.htmlEqual(
target.innerHTML,
`
<div>20 x 40</div>
<div>30 x 50</div>
<div>20 x 40</div>
<div>30 x 50</div>
`
);
component.boxes = [
{ width: 80, height: 70 },
{ width: 90, height: 60 }
];
assert.htmlEqual(
target.innerHTML,
`
<div>80 x 70</div>
<div>90 x 60</div>
<div>80 x 70</div>
<div>90 x 60</div>
`
);
component.boxes = [];
assert.htmlEqual(target.innerHTML, '');
}
};

@ -0,0 +1,29 @@
<script>
export let boxes;
function fade() {
return {
duration: 0,
}
}
</script>
{#if boxes.length > 1}
{@const box1 = boxes[0]}
{@const box2 = boxes[1]}
{@const { width, height } = box1}
<div>{width} x {height}</div>
<div>{box2.width} x {box2.height}</div>
{:else if boxes.length > 0}
{@const box = boxes[0]}
{@const { width, height } = box}
<div out:fade>{width} x {height}</div>
{/if}
{#if boxes.length > 1}
<div>{boxes[0].width} x {boxes[0].height}</div>
<div>{boxes[1].width} x {boxes[1].height}</div>
{:else if boxes.length > 0}
{@const box = boxes[0]}
{@const { width, height } = box}
<div out:fade>{width} x {height}</div>
{/if}

@ -0,0 +1,43 @@
export default {
html: '<div>20 x 40</div>',
props: {
boxes: [{ width: 20, height: 40 }]
},
async test({ component, target, assert }) {
component.boxes = [{ width: 30, height: 60 }];
assert.htmlEqual(target.innerHTML, `
<div>30 x 60</div>
`);
component.boxes = [
{ width: 20, height: 40 },
{ width: 30, height: 50 }
];
assert.htmlEqual(target.innerHTML, `
<div>20 x 40</div>
<div>30 x 50</div>
`);
component.boxes = [
{ width: 80, height: 70 },
{ width: 90, height: 60 }
];
assert.htmlEqual(target.innerHTML, `
<div>80 x 70</div>
<div>90 x 60</div>
`);
component.boxes = [
{ width: 20, height: 40 },
{ width: 30, height: 50 },
{ width: 30, height: 50 }
];
assert.htmlEqual(target.innerHTML, '<div>3</div>');
component.boxes = [];
assert.htmlEqual(target.innerHTML, '<div>0</div>');
}
};

@ -0,0 +1,18 @@
<script>
export let boxes;
</script>
{#if boxes.length === 2}
{@const box1 = boxes[0]}
{@const box2 = boxes[1]}
{@const { width, height } = box1}
<div>{width} x {height}</div>
<div>{box2.width} x {box2.height}</div>
{:else if boxes.length === 1}
{@const box = boxes[0]}
{@const { width, height } = box}
<div>{width} x {height}</div>
{:else}
{@const length = boxes.length}
<div>{length}</div>
{/if}

@ -0,0 +1,28 @@
export default {
html: '<div>10 x 34</div>',
props: {
boxes: [{ width: 10, height: 34 }]
},
async test({ component, target, assert }) {
component.boxes = [{ width: 20, height: 40 }];
assert.htmlEqual(
target.innerHTML,
`
<div>20 x 40</div>
`
);
component.boxes = [];
assert.htmlEqual(target.innerHTML, '');
component.boxes = [{ width: 18, height: 48 }];
assert.htmlEqual(
target.innerHTML,
`
<div>18 x 48</div>
`
);
}
};

@ -0,0 +1,9 @@
<script>
export let boxes;
</script>
{#if boxes.length > 0}
{@const box = boxes[0]}
{@const { width, height } = box}
<div>{width} x {height}</div>
{/if}

@ -1,7 +1,7 @@
[ [
{ {
"code": "invalid-const-placement", "code": "invalid-const-placement",
"message": "{@const} must be the immediate child of {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>", "message": "{@const} must be the immediate child of {#if}, {:else if}, {:else}, {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>",
"start": { "line": 5, "column": 0, "character": 36 }, "start": { "line": 5, "column": 0, "character": 36 },
"end": { "line": 5, "column": 18, "character": 54 }, "end": { "line": 5, "column": 18, "character": 54 },
"pos": 36 "pos": 36

@ -1,9 +1,9 @@
[ [
{ {
"code": "invalid-const-placement", "code": "invalid-const-placement",
"message": "{@const} must be the immediate child of {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>", "message": "{@const} must be the immediate child of {#if}, {:else if}, {:else}, {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>",
"start": { "line": 6, "column": 2, "character": 46 }, "start": { "line": 7, "column": 4, "character": 63 },
"end": { "line": 6, "column": 20, "character": 64 }, "end": { "line": 7, "column": 18, "character": 77 },
"pos": 46 "pos": 63
} }
] ]

@ -2,6 +2,8 @@
export let a; export let a;
</script> </script>
{#if a} {#each a as i}
{@const b = a + 1} <div>
{/if} {@const b = i}
</div>
{/each}

@ -1,9 +0,0 @@
[
{
"code": "invalid-const-placement",
"message": "{@const} must be the immediate child of {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>",
"start": { "line": 7, "column": 4, "character": 63 },
"end": { "line": 7, "column": 18, "character": 77 },
"pos": 63
}
]

@ -1,9 +0,0 @@
<script>
export let a;
</script>
{#each a as i}
<div>
{@const b = i}
</div>
{/each}
Loading…
Cancel
Save