'
},
invalid_const_declaration: (name: string) => ({
code: 'invalid-const-declaration',
diff --git a/src/compiler/compile/nodes/ConstTag.ts b/src/compiler/compile/nodes/ConstTag.ts
index 157361fb67..87a9039008 100644
--- a/src/compiler/compile/nodes/ConstTag.ts
+++ b/src/compiler/compile/nodes/ConstTag.ts
@@ -11,7 +11,7 @@ import is_reference, { NodeWithPropertyDefinition } from 'is-reference';
import get_object from '../utils/get_object';
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 {
type: 'ConstTag';
diff --git a/src/compiler/compile/nodes/ElseBlock.ts b/src/compiler/compile/nodes/ElseBlock.ts
index db5c649845..fb3661c7ab 100644
--- a/src/compiler/compile/nodes/ElseBlock.ts
+++ b/src/compiler/compile/nodes/ElseBlock.ts
@@ -1,16 +1,21 @@
-import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock';
import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
import { TemplateNode } from '../../interfaces';
import Node from './shared/Node';
+import ConstTag from './ConstTag';
+import get_const_tags from './shared/get_const_tags';
export default class ElseBlock extends AbstractBlock {
type: 'ElseBlock';
+ scope: TemplateScope;
+ const_tags: ConstTag[];
constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
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();
}
diff --git a/src/compiler/compile/nodes/IfBlock.ts b/src/compiler/compile/nodes/IfBlock.ts
index 85d5d6b36e..8351f1e0e2 100644
--- a/src/compiler/compile/nodes/IfBlock.ts
+++ b/src/compiler/compile/nodes/IfBlock.ts
@@ -1,22 +1,26 @@
import ElseBlock from './ElseBlock';
import Expression from './shared/Expression';
-import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock';
import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
import { TemplateNode } from '../../interfaces';
import Node from './shared/Node';
+import ConstTag from './ConstTag';
+import get_const_tags from './shared/get_const_tags';
export default class IfBlock extends AbstractBlock {
type: 'IfBlock';
expression: Expression;
else: ElseBlock;
+ scope: TemplateScope;
+ const_tags: ConstTag[];
constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info);
+ this.scope = scope.child();
- this.expression = new Expression(component, this, scope, info.expression);
- this.children = map_children(component, this, scope, info.children);
+ this.expression = new Expression(component, this, this.scope, info.expression);
+ ([this.const_tags, this.children] = get_const_tags(info.children, component, this, this));
this.else = info.else
? new ElseBlock(component, this, scope, info.else)
diff --git a/src/compiler/compile/nodes/interfaces.ts b/src/compiler/compile/nodes/interfaces.ts
index 2c636b36f3..f023cad25c 100644
--- a/src/compiler/compile/nodes/interfaces.ts
+++ b/src/compiler/compile/nodes/interfaces.ts
@@ -72,6 +72,8 @@ export type INode = Action
| Window;
export type INodeAllowConstTag =
+| IfBlock
+| ElseBlock
| EachBlock
| CatchBlock
| ThenBlock
diff --git a/src/compiler/compile/render_dom/wrappers/EachBlock.ts b/src/compiler/compile/render_dom/wrappers/EachBlock.ts
index b77b2472df..db7929e51b 100644
--- a/src/compiler/compile/render_dom/wrappers/EachBlock.ts
+++ b/src/compiler/compile/render_dom/wrappers/EachBlock.ts
@@ -27,6 +27,7 @@ export class ElseBlockWrapper extends Wrapper {
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
+ add_const_tags_context(renderer, this.node.const_tags);
this.block = block.child({
comment: create_debugging_comment(node, this.renderer.component),
@@ -257,6 +258,18 @@ export default class EachBlockWrapper extends Wrapper {
}
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`);
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
block.chunks.init.push(b`
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) {
this.updates.push(b`
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}) {
- ${each_block_else} = ${this.else.block.name}(#ctx);
+ ${each_block_else} = ${this.else.block.name}(${else_ctx});
${each_block_else}.c();
${has_transitions && b`@transition_in(${each_block_else}, 1);`}
${each_block_else}.m(${update_mount_node}, ${update_anchor_node});
@@ -321,7 +334,7 @@ export default class EachBlockWrapper extends Wrapper {
${destroy_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();
${has_transitions && b`@transition_in(${each_block_else}, 1);`}
${each_block_else}.m(${update_mount_node}, ${update_anchor_node});
diff --git a/src/compiler/compile/render_dom/wrappers/IfBlock.ts b/src/compiler/compile/render_dom/wrappers/IfBlock.ts
index e94404290f..d74ce270bf 100644
--- a/src/compiler/compile/render_dom/wrappers/IfBlock.ts
+++ b/src/compiler/compile/render_dom/wrappers/IfBlock.ts
@@ -11,6 +11,7 @@ import { walk } from 'estree-walker';
import { is_head } from './shared/is_head';
import { Identifier, Node } from 'estree';
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) {
return (
@@ -25,8 +26,10 @@ class IfBlockBranch extends Wrapper {
condition?: any;
snippet?: Node;
is_dynamic: boolean;
+ node: IfBlock | ElseBlock;
var = null;
+ get_ctx_name: Node | undefined;
constructor(
renderer: Renderer,
@@ -63,6 +66,8 @@ class IfBlockBranch extends Wrapper {
}
}
+ 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(
@@ -74,6 +79,10 @@ class IfBlockBranch extends Wrapper {
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(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_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';
@@ -260,9 +281,13 @@ export default class IfBlockWrapper extends Wrapper {
) {
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}(#ctx)`
- : x`${current_block_type} && ${current_block_type}(#ctx)`;
+ ? x`${current_block_type}(${if_ctx})`
+ : x`${current_block_type} && ${current_block_type}(${if_ctx})`;
if (this.needs_update) {
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}`;
})}
${this.branches.map(({ condition, snippet, block }) => condition
- ? b`
- ${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`}
- if (${condition}) return ${block.name};`
- : b`return ${block.name};`
+ ? b`
+ ${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`}
+ if (${condition}) return ${block.name};`
+ : b`return ${block.name};`
)}
}
`);
@@ -282,12 +307,40 @@ export default class IfBlockWrapper extends Wrapper {
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};`)}
+ ? 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};
@@ -322,7 +375,7 @@ export default class IfBlockWrapper extends Wrapper {
if (dynamic) {
block.chunks.update.push(b`
if (${current_block_type} === (${current_block_type} = ${select_block_type}(#ctx, #dirty)) && ${name}) {
- ${name}.p(#ctx, #dirty);
+ ${name}.p(${if_ctx}, #dirty);
} else {
${change_block}
}
@@ -336,9 +389,9 @@ export default class IfBlockWrapper extends Wrapper {
}
} else if (dynamic) {
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 {
- 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 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
@@ -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}`;
})}
${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`
+ ${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};`)}
+ ? 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}](#ctx);
+ ${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}](#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`
${name} = ${if_blocks}[${current_block_type_index}];
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();
} else {
- ${dynamic && b`${name}.p(#ctx, #dirty);`}
+ ${dynamic && b`${name}.p(${if_ctx}, #dirty);`}
}
${has_transitions && b`@transition_in(${name}, 1);`}
${name}.m(${update_mount_node}, ${anchor});
@@ -480,7 +564,7 @@ export default class IfBlockWrapper extends Wrapper {
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(#ctx, #dirty);`)}
+ ${if_current_block_type_index(b`${if_blocks}[${current_block_type_index}].p(${if_ctx}, #dirty);`)}
} else {
${change_block}
}
@@ -494,9 +578,9 @@ export default class IfBlockWrapper extends Wrapper {
}
} else if (dynamic) {
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 {
- 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
) {
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}(#ctx);
+ let ${name} = ${branch.condition} && ${branch.block.name}(${if_ctx});
`);
const initial_mount_node = parent_node || '#target';
@@ -533,15 +618,14 @@ export default class IfBlockWrapper extends Wrapper {
const enter = b`
if (${name}) {
- ${dynamic && b`${name}.p(#ctx, #dirty);`}
- ${
- has_transitions &&
+ ${dynamic && b`${name}.p(${if_ctx}, #dirty);`}
+ ${has_transitions &&
b`if (${block.renderer.dirty(branch.dependencies)}) {
- @transition_in(${name}, 1);
- }`
- }
+ @transition_in(${name}, 1);
+ }`
+ }
} else {
- ${name} = ${branch.block.name}(#ctx);
+ ${name} = ${branch.block.name}(${if_ctx});
${name}.c();
${has_transitions && b`@transition_in(${name}, 1);`}
${name}.m(${update_mount_node}, ${anchor});
@@ -578,7 +662,7 @@ export default class IfBlockWrapper extends Wrapper {
}
} else if (dynamic) {
block.chunks.update.push(b`
- if (${branch.condition}) ${name}.p(#ctx, #dirty);
+ if (${branch.condition}) ${name}.p(${if_ctx}, #dirty);
`);
}
diff --git a/src/compiler/compile/render_ssr/handlers/EachBlock.ts b/src/compiler/compile/render_ssr/handlers/EachBlock.ts
index 008c023e11..2b9149ffef 100644
--- a/src/compiler/compile/render_ssr/handlers/EachBlock.ts
+++ b/src/compiler/compile/render_ssr/handlers/EachBlock.ts
@@ -2,6 +2,7 @@ import Renderer, { RenderOptions } from '../Renderer';
import EachBlock from '../../nodes/EachBlock';
import { x } from 'code-red';
import { get_const_tags } from './shared/get_const_tags';
+import { Node } from 'estree';
export default function(node: EachBlock, renderer: Renderer, options: RenderOptions) {
const args = [node.context_node];
@@ -16,7 +17,8 @@ export default function(node: EachBlock, renderer: Renderer, options: RenderOpti
if (node.else) {
renderer.push();
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}`);
} else {
diff --git a/src/compiler/compile/render_ssr/handlers/IfBlock.ts b/src/compiler/compile/render_ssr/handlers/IfBlock.ts
index 504a237bc5..b242775fe6 100644
--- a/src/compiler/compile/render_ssr/handlers/IfBlock.ts
+++ b/src/compiler/compile/render_ssr/handlers/IfBlock.ts
@@ -1,17 +1,21 @@
import IfBlock from '../../nodes/IfBlock';
import Renderer, { RenderOptions } from '../Renderer';
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;
renderer.push();
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();
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}`);
}
diff --git a/test/runtime/samples/const-tag-each-else/_config.js b/test/runtime/samples/const-tag-each-else/_config.js
new file mode 100644
index 0000000000..d95be09ccc
--- /dev/null
+++ b/test/runtime/samples/const-tag-each-else/_config.js
@@ -0,0 +1,26 @@
+export default {
+ html: `
+ 12 120 70, 30+4=34
+ 35 350 120, 50+7=57
+ 48 480 140, 60+8=68
+ `,
+ async test({ component, target, assert }) {
+ component.boxes = [];
+ assert.htmlEqual(target.innerHTML, `
+ 10 * 2 = 20
+ `);
+
+ component.constant = 35;
+ assert.htmlEqual(target.innerHTML, `
+ 35 * 2 = 70
+ `);
+
+ component.boxes = [
+ {width: 3, height: 4}
+ ];
+
+ assert.htmlEqual(target.innerHTML, `
+ 12 420 245, 105+4=109
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-each-else/main.svelte b/test/runtime/samples/const-tag-each-else/main.svelte
new file mode 100644
index 0000000000..7701a579cf
--- /dev/null
+++ b/test/runtime/samples/const-tag-each-else/main.svelte
@@ -0,0 +1,22 @@
+
+
+{#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]}
+ {area} {volume} {perimeter}, {width}+{height}={sum}
+{:else}
+ {@const double = constant + constant}
+ {constant} * 2 = {double}
+{/each}
\ No newline at end of file
diff --git a/test/runtime/samples/const-tag-if-else-if/_config.js b/test/runtime/samples/const-tag-if-else-if/_config.js
new file mode 100644
index 0000000000..072eb2e44e
--- /dev/null
+++ b/test/runtime/samples/const-tag-if-else-if/_config.js
@@ -0,0 +1,56 @@
+export default {
+ html: `
+ 20 x 40
+ 20 x 40
+ `,
+ props: {
+ boxes: [{ width: 20, height: 40 }]
+ },
+ async test({ component, target, assert }) {
+ component.boxes = [{ width: 40, height: 70 }];
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ 40 x 70
+ 40 x 70
+ `
+ );
+
+ component.boxes = [];
+
+ assert.htmlEqual(target.innerHTML, '');
+
+ component.boxes = [
+ { width: 20, height: 40 },
+ { width: 30, height: 50 }
+ ];
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ 20 x 40
+ 30 x 50
+ 20 x 40
+ 30 x 50
+ `
+ );
+
+ component.boxes = [
+ { width: 80, height: 70 },
+ { width: 90, height: 60 }
+ ];
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ 80 x 70
+ 90 x 60
+ 80 x 70
+ 90 x 60
+ `
+ );
+
+ component.boxes = [];
+ assert.htmlEqual(target.innerHTML, '');
+ }
+};
diff --git a/test/runtime/samples/const-tag-if-else-if/main.svelte b/test/runtime/samples/const-tag-if-else-if/main.svelte
new file mode 100644
index 0000000000..8188625897
--- /dev/null
+++ b/test/runtime/samples/const-tag-if-else-if/main.svelte
@@ -0,0 +1,24 @@
+
+
+{#if boxes.length > 1}
+ {@const box1 = boxes[0]}
+ {@const box2 = boxes[1]}
+ {@const { width, height } = box1}
+ {width} x {height}
+ {box2.width} x {box2.height}
+{:else if boxes.length > 0}
+ {@const box = boxes[0]}
+ {@const { width, height } = box}
+ {width} x {height}
+{/if}
+
+{#if boxes.length > 1}
+ {boxes[0].width} x {boxes[0].height}
+ {boxes[1].width} x {boxes[1].height}
+{:else if boxes.length > 0}
+ {@const box = boxes[0]}
+ {@const { width, height } = box}
+ {width} x {height}
+{/if}
\ No newline at end of file
diff --git a/test/runtime/samples/const-tag-if-else-outro/_config.js b/test/runtime/samples/const-tag-if-else-outro/_config.js
new file mode 100644
index 0000000000..56c7fc382b
--- /dev/null
+++ b/test/runtime/samples/const-tag-if-else-outro/_config.js
@@ -0,0 +1,57 @@
+export default {
+ html: `
+ 20 x 40
+ 20 x 40
+ `,
+ props: {
+ boxes: [{ width: 20, height: 40 }]
+ },
+ async test({ component, target, assert, raf }) {
+ component.boxes = [{ width: 40, height: 70 }];
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ 40 x 70
+ 40 x 70
+ `
+ );
+
+ component.boxes = [];
+
+ raf.tick(0);
+ assert.htmlEqual(target.innerHTML, '');
+
+ component.boxes = [
+ { width: 20, height: 40 },
+ { width: 30, height: 50 }
+ ];
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ 20 x 40
+ 30 x 50
+ 20 x 40
+ 30 x 50
+ `
+ );
+
+ component.boxes = [
+ { width: 80, height: 70 },
+ { width: 90, height: 60 }
+ ];
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ 80 x 70
+ 90 x 60
+ 80 x 70
+ 90 x 60
+ `
+ );
+
+ component.boxes = [];
+ assert.htmlEqual(target.innerHTML, '');
+ }
+};
diff --git a/test/runtime/samples/const-tag-if-else-outro/main.svelte b/test/runtime/samples/const-tag-if-else-outro/main.svelte
new file mode 100644
index 0000000000..90ac27fec1
--- /dev/null
+++ b/test/runtime/samples/const-tag-if-else-outro/main.svelte
@@ -0,0 +1,29 @@
+
+
+{#if boxes.length > 1}
+ {@const box1 = boxes[0]}
+ {@const box2 = boxes[1]}
+ {@const { width, height } = box1}
+ {width} x {height}
+ {box2.width} x {box2.height}
+{:else if boxes.length > 0}
+ {@const box = boxes[0]}
+ {@const { width, height } = box}
+ {width} x {height}
+{/if}
+
+{#if boxes.length > 1}
+ {boxes[0].width} x {boxes[0].height}
+ {boxes[1].width} x {boxes[1].height}
+{:else if boxes.length > 0}
+ {@const box = boxes[0]}
+ {@const { width, height } = box}
+ {width} x {height}
+{/if}
\ No newline at end of file
diff --git a/test/runtime/samples/const-tag-if-else/_config.js b/test/runtime/samples/const-tag-if-else/_config.js
new file mode 100644
index 0000000000..13e59bf836
--- /dev/null
+++ b/test/runtime/samples/const-tag-if-else/_config.js
@@ -0,0 +1,43 @@
+export default {
+ html: '20 x 40
',
+ props: {
+ boxes: [{ width: 20, height: 40 }]
+ },
+ async test({ component, target, assert }) {
+ component.boxes = [{ width: 30, height: 60 }];
+
+ assert.htmlEqual(target.innerHTML, `
+ 30 x 60
+ `);
+
+ component.boxes = [
+ { width: 20, height: 40 },
+ { width: 30, height: 50 }
+ ];
+
+ assert.htmlEqual(target.innerHTML, `
+ 20 x 40
+ 30 x 50
+ `);
+
+ component.boxes = [
+ { width: 80, height: 70 },
+ { width: 90, height: 60 }
+ ];
+
+ assert.htmlEqual(target.innerHTML, `
+ 80 x 70
+ 90 x 60
+ `);
+
+ component.boxes = [
+ { width: 20, height: 40 },
+ { width: 30, height: 50 },
+ { width: 30, height: 50 }
+ ];
+ assert.htmlEqual(target.innerHTML, '3
');
+
+ component.boxes = [];
+ assert.htmlEqual(target.innerHTML, '0
');
+ }
+};
diff --git a/test/runtime/samples/const-tag-if-else/main.svelte b/test/runtime/samples/const-tag-if-else/main.svelte
new file mode 100644
index 0000000000..1da3d4ea66
--- /dev/null
+++ b/test/runtime/samples/const-tag-if-else/main.svelte
@@ -0,0 +1,18 @@
+
+
+{#if boxes.length === 2}
+ {@const box1 = boxes[0]}
+ {@const box2 = boxes[1]}
+ {@const { width, height } = box1}
+ {width} x {height}
+ {box2.width} x {box2.height}
+{:else if boxes.length === 1}
+ {@const box = boxes[0]}
+ {@const { width, height } = box}
+ {width} x {height}
+{:else}
+ {@const length = boxes.length}
+ {length}
+{/if}
diff --git a/test/runtime/samples/const-tag-if/_config.js b/test/runtime/samples/const-tag-if/_config.js
new file mode 100644
index 0000000000..019584d016
--- /dev/null
+++ b/test/runtime/samples/const-tag-if/_config.js
@@ -0,0 +1,28 @@
+export default {
+ html: '10 x 34
',
+ props: {
+ boxes: [{ width: 10, height: 34 }]
+ },
+ async test({ component, target, assert }) {
+ component.boxes = [{ width: 20, height: 40 }];
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ 20 x 40
+ `
+ );
+
+ component.boxes = [];
+ assert.htmlEqual(target.innerHTML, '');
+
+ component.boxes = [{ width: 18, height: 48 }];
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ 18 x 48
+ `
+ );
+ }
+};
diff --git a/test/runtime/samples/const-tag-if/main.svelte b/test/runtime/samples/const-tag-if/main.svelte
new file mode 100644
index 0000000000..8f10ceefb8
--- /dev/null
+++ b/test/runtime/samples/const-tag-if/main.svelte
@@ -0,0 +1,9 @@
+
+
+{#if boxes.length > 0}
+ {@const box = boxes[0]}
+ {@const { width, height } = box}
+ {width} x {height}
+{/if}
diff --git a/test/validator/samples/const-tag-placement-1/errors.json b/test/validator/samples/const-tag-placement-1/errors.json
index 52de84e2be..5291b3a351 100644
--- a/test/validator/samples/const-tag-placement-1/errors.json
+++ b/test/validator/samples/const-tag-placement-1/errors.json
@@ -1,7 +1,7 @@
[
{
"code": "invalid-const-placement",
- "message": "{@const} must be the immediate child of {#each}, {:then}, {:catch}, or ",
+ "message": "{@const} must be the immediate child of {#if}, {:else if}, {:else}, {#each}, {:then}, {:catch}, or ",
"start": { "line": 5, "column": 0, "character": 36 },
"end": { "line": 5, "column": 18, "character": 54 },
"pos": 36
diff --git a/test/validator/samples/const-tag-placement-2/errors.json b/test/validator/samples/const-tag-placement-2/errors.json
index 005b3ff8b7..9922063a1d 100644
--- a/test/validator/samples/const-tag-placement-2/errors.json
+++ b/test/validator/samples/const-tag-placement-2/errors.json
@@ -1,9 +1,9 @@
[
{
"code": "invalid-const-placement",
- "message": "{@const} must be the immediate child of {#each}, {:then}, {:catch}, or ",
- "start": { "line": 6, "column": 2, "character": 46 },
- "end": { "line": 6, "column": 20, "character": 64 },
- "pos": 46
+ "message": "{@const} must be the immediate child of {#if}, {:else if}, {:else}, {#each}, {:then}, {:catch}, or ",
+ "start": { "line": 7, "column": 4, "character": 63 },
+ "end": { "line": 7, "column": 18, "character": 77 },
+ "pos": 63
}
]
diff --git a/test/validator/samples/const-tag-placement-2/input.svelte b/test/validator/samples/const-tag-placement-2/input.svelte
index a843f9d58f..2519482a98 100644
--- a/test/validator/samples/const-tag-placement-2/input.svelte
+++ b/test/validator/samples/const-tag-placement-2/input.svelte
@@ -2,6 +2,8 @@
export let a;
-{#if a}
- {@const b = a + 1}
-{/if}
\ No newline at end of file
+{#each a as i}
+
+ {@const b = i}
+
+{/each}
\ No newline at end of file
diff --git a/test/validator/samples/const-tag-placement-3/errors.json b/test/validator/samples/const-tag-placement-3/errors.json
deleted file mode 100644
index cc0ba4d74d..0000000000
--- a/test/validator/samples/const-tag-placement-3/errors.json
+++ /dev/null
@@ -1,9 +0,0 @@
-[
- {
- "code": "invalid-const-placement",
- "message": "{@const} must be the immediate child of {#each}, {:then}, {:catch}, or ",
- "start": { "line": 7, "column": 4, "character": 63 },
- "end": { "line": 7, "column": 18, "character": 77 },
- "pos": 63
- }
-]
diff --git a/test/validator/samples/const-tag-placement-3/input.svelte b/test/validator/samples/const-tag-placement-3/input.svelte
deleted file mode 100644
index 2519482a98..0000000000
--- a/test/validator/samples/const-tag-placement-3/input.svelte
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-{#each a as i}
-
- {@const b = i}
-
-{/each}
\ No newline at end of file