Skip to content

Commit

Permalink
[feat] support const tag for if block (#7451)
Browse files Browse the repository at this point in the history
  • Loading branch information
tanhauhau committed Apr 30, 2022
1 parent 61d1467 commit a2de389
Show file tree
Hide file tree
Showing 25 changed files with 490 additions and 80 deletions.
6 changes: 3 additions & 3 deletions site/content/docs/02-template-syntax.md
Expand Up @@ -465,16 +465,16 @@ The `{@const ...}` tag defines a local constant.

```sv
<script>
export let boxes;
export let boxes;
</script>
{#each boxes as box}
{@const area = box.width * box.height}
{@const area = box.width * box.height}
{box.width} * {box.height} = {area}
{/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
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/compiler_errors.ts
Expand Up @@ -260,7 +260,7 @@ export default {
},
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) => ({
code: 'invalid-const-declaration',
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/nodes/ConstTag.ts
Expand Up @@ -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';
Expand Down
9 changes: 7 additions & 2 deletions 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();
}
Expand Down
10 changes: 7 additions & 3 deletions 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)
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/compile/nodes/interfaces.ts
Expand Up @@ -72,6 +72,8 @@ export type INode = Action
| Window;

export type INodeAllowConstTag =
| IfBlock
| ElseBlock
| EachBlock
| CatchBlock
| ThenBlock
Expand Down
21 changes: 17 additions & 4 deletions src/compiler/compile/render_dom/wrappers/EachBlock.ts
Expand Up @@ -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),
Expand Down Expand Up @@ -257,14 +258,26 @@ 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;`);

// 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});
}
`);

Expand Down Expand Up @@ -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});
Expand All @@ -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});
Expand Down

0 comments on commit a2de389

Please sign in to comment.