Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] support const tag for if block #7451

Merged
merged 5 commits into from Apr 30, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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}, {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>'
tanhauhau marked this conversation as resolved.
Show resolved Hide resolved
},
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
150 changes: 117 additions & 33 deletions src/compiler/compile/render_dom/wrappers/IfBlock.ts
Expand Up @@ -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 (
Expand All @@ -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,
Expand Down Expand Up @@ -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(
Expand All @@ -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');
}
}
}

Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -260,34 +281,66 @@ 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`
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}`;
})}
return b`${snippet && dependencies.length > 0 ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = null;` : null}`;
})}
${this.branches.map(({ condition, snippet, block }) => condition
? b`
? b`
${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`}
if (${condition}) return ${block.name};`
: b`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};`)}
? 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};
Expand Down Expand Up @@ -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}
}
Expand All @@ -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);`);
}
}

Expand Down Expand Up @@ -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
Expand All @@ -389,35 +445,63 @@ export default class IfBlockWrapper extends Wrapper {
? 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}`;
})}
return b`${snippet && dependencies.length > 0 ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = null;` : null}`;
})}
${this.branches.map(({ condition, snippet }, i) => condition
? b`
? b`
${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`}
if (${condition}) return ${i};`
: b`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});
}
`);
}
Expand Down Expand Up @@ -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});
Expand Down Expand Up @@ -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}
}
Expand All @@ -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);`);
}
}

Expand All @@ -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';
Expand All @@ -533,15 +618,14 @@ export default class IfBlockWrapper extends Wrapper {

const enter = b`
if (${name}) {
${dynamic && b`${name}.p(#ctx, #dirty);`}
${
has_transitions &&
b`if (${block.renderer.dirty(branch.dependencies)}) {
${dynamic && b`${name}.p(${if_ctx}, #dirty);`}
${has_transitions &&
b`if (${block.renderer.dirty(branch.dependencies)}) {
@transition_in(${name}, 1);
}`
}
}
benmccann marked this conversation as resolved.
Show resolved Hide resolved
} 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});
Expand Down Expand Up @@ -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);
`);
}

Expand Down