diff --git a/src/compiler/compile/nodes/ConstTag.ts b/src/compiler/compile/nodes/ConstTag.ts index 44a50aa005..4c6a040f88 100644 --- a/src/compiler/compile/nodes/ConstTag.ts +++ b/src/compiler/compile/nodes/ConstTag.ts @@ -12,7 +12,7 @@ import get_object from '../utils/get_object'; import compiler_errors from '../compiler_errors'; import { Node as ESTreeNode } from 'estree'; -const allowed_parents = new Set(['EachBlock', 'CatchBlock', 'ThenBlock', 'InlineComponent', 'SlotTemplate', 'IfBlock', 'ElseBlock']); +const allowed_parents = new Set(['EachBlock', 'CatchBlock', 'ThenBlock', 'InlineComponent', 'SlotTemplate', 'IfBlock', 'ElseBlock', 'SlotTemplateIfBlock', 'SlotTemplateElseBlock']); export default class ConstTag extends Node { type: 'ConstTag'; diff --git a/src/compiler/compile/nodes/InlineComponent.ts b/src/compiler/compile/nodes/InlineComponent.ts index 41ffce83ec..2f39b6faff 100644 --- a/src/compiler/compile/nodes/InlineComponent.ts +++ b/src/compiler/compile/nodes/InlineComponent.ts @@ -12,6 +12,7 @@ import { TemplateNode } from '../../interfaces'; import compiler_errors from '../compiler_errors'; import { regex_only_whitespaces } from '../../utils/patterns'; import { validate_get_slot_names } from './SlotTemplateIfBlock'; +import { BaseNode } from 'estree-walker'; export default class InlineComponent extends Node { type: 'InlineComponent'; @@ -147,7 +148,7 @@ export default class InlineComponent extends Node { } else if (child.type === 'Comment' && children.length > 0) { children[children.length - 1].children.unshift(child); info.children.splice(i, 1); - } else if (child.type === 'IfBlock' && child.children.some(if_child => if_child.type === 'SlotTemplate')) { + } else if (child.type === 'IfBlock' && if_block_contains_slot_template(child)) { children.push({ ...child, type: 'SlotTemplateIfBlock' @@ -193,3 +194,11 @@ function get_namespace(parent: Node, explicit_namespace: string) { return parent_element.namespace; } + +function if_block_contains_slot_template(node: TemplateNode) { + for (const child of node.children) { + if (child.type === 'SlotTemplate') return true; + if (child.type === 'IfBlock' && if_block_contains_slot_template(child)) return true; + } + return false; +} \ No newline at end of file diff --git a/src/compiler/compile/nodes/SlotTemplate.ts b/src/compiler/compile/nodes/SlotTemplate.ts index 7b28afb47d..2dd024da78 100644 --- a/src/compiler/compile/nodes/SlotTemplate.ts +++ b/src/compiler/compile/nodes/SlotTemplate.ts @@ -69,6 +69,7 @@ export default class SlotTemplate extends Node { let parent = this.parent; while (parent.type === 'SlotTemplateIfBlock' || parent.type === 'SlotTemplateElseBlock') parent = parent.parent; if (parent.type === 'IfBlock' || parent.type === 'ElseBlock') { + console.log({ parent }); return this.component.error(this, compiler_errors.invalid_mix_element_and_conditional_slot); } if (parent.type !== 'InlineComponent') { diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index caaf0ac7d3..0522721359 100644 --- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts @@ -173,7 +173,7 @@ export default class InlineComponentWrapper extends Wrapper { : this.children.length > 0 ? [ p`$$slots: { - ${this.children.map((slot: SlotTemplateWrapper) => p`${slot.slot_template_name}: ${slot.slot_definition}`)} + ${this.children.filter((slot: SlotTemplateWrapper) => slot.slot_definition).map((slot: SlotTemplateWrapper) => p`${slot.slot_template_name}: ${slot.slot_definition}`)} }`, p`$$scope: { ctx: #ctx }` ] diff --git a/src/compiler/compile/render_dom/wrappers/SlotTemplate.ts b/src/compiler/compile/render_dom/wrappers/SlotTemplate.ts index dd832f0f42..4f59ca34d4 100644 --- a/src/compiler/compile/render_dom/wrappers/SlotTemplate.ts +++ b/src/compiler/compile/render_dom/wrappers/SlotTemplate.ts @@ -13,6 +13,7 @@ import SlotTemplate from '../../nodes/SlotTemplate'; import { add_const_tags, add_const_tags_context } from './shared/add_const_tags'; import TemplateScope from '../../nodes/shared/TemplateScope'; import SlotTemplateIfBlockWrapper from './SlotTemplateIfBlock'; +import { INode } from '../../nodes/interfaces'; export default class SlotTemplateWrapper extends Wrapper { node: SlotTemplate; @@ -83,7 +84,7 @@ export default class SlotTemplateWrapper extends Wrapper { this.render_get_context(); } - if (!this.block.has_content()) { + if (this.slot_template_name === 'default' && !this.block.has_content()) { this.renderer.remove_block(this.block); this.slot_definition = null; } @@ -100,10 +101,23 @@ export default class SlotTemplateWrapper extends Wrapper { } render_get_context() { + const if_const_tags = []; + let parent = this.node.parent; + while (parent.type === 'SlotTemplateIfBlock' || parent.type === 'SlotTemplateElseBlock') { + if_const_tags.push(parent.const_tags); + if (parent.type === 'SlotTemplateElseBlock') parent = parent.parent; + parent = parent.parent; + } + const const_tags = []; + for (let i = if_const_tags.length - 1; i >= 0; i--) { + const_tags.push(...if_const_tags[i]); + } + const_tags.push(...this.node.const_tags); + const get_context = this.block.renderer.component.get_unique_name('get_context'); this.block.renderer.blocks.push(b` function ${get_context}(#ctx) { - ${add_const_tags(this.block, this.node.const_tags, '#ctx')} + ${add_const_tags(this.block, const_tags, '#ctx')} } `); this.block.chunks.declarations.push(b`${get_context}(#ctx)`); diff --git a/src/compiler/compile/render_dom/wrappers/SlotTemplateIfBlock.ts b/src/compiler/compile/render_dom/wrappers/SlotTemplateIfBlock.ts index 7f0cb129ee..8acb61df04 100644 --- a/src/compiler/compile/render_dom/wrappers/SlotTemplateIfBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/SlotTemplateIfBlock.ts @@ -5,9 +5,10 @@ import { Identifier } from 'estree'; import SlotTemplateIfBlock from '../../nodes/SlotTemplateIfBlock'; import SlotTemplateWrapper from './SlotTemplate'; import SlotTemplate from '../../nodes/SlotTemplate'; -import { b } from 'code-red'; +import { x, b } from 'code-red'; import InlineComponentWrapper from './InlineComponent'; import TemplateScope from '../../nodes/shared/TemplateScope'; +import { add_const_tags, add_const_tags_context } from './shared/add_const_tags'; export default class SlotTemplateIfBlockWrapper extends Wrapper { node: SlotTemplateIfBlock; @@ -30,6 +31,8 @@ export default class SlotTemplateIfBlockWrapper extends Wrapper { super(renderer, block, parent, node); this.scope = node.scope; + add_const_tags_context(renderer, this.node.const_tags); + for (const child of this.node.children) { if (child.type === 'SlotTemplate') { this.children.push(new SlotTemplateWrapper(renderer, block, this, child as SlotTemplate, strip_whitespace, next_sibling)); @@ -39,6 +42,7 @@ export default class SlotTemplateIfBlockWrapper extends Wrapper { } if (node.else) { + add_const_tags_context(renderer, this.node.else.const_tags); for (const child of node.else.children) { if (child.type === 'SlotTemplate') { this.else.push(new SlotTemplateWrapper(renderer, block, this, child as SlotTemplate, strip_whitespace, next_sibling)); @@ -55,11 +59,33 @@ export default class SlotTemplateIfBlockWrapper extends Wrapper { } render_slot_template_definition(block: Block) { + let if_get_context; + let else_get_context; + + if (this.node.const_tags.length) { + if_get_context = block.renderer.component.get_unique_name('get_context'); + block.renderer.blocks.push(b` + function ${if_get_context}(#ctx) { + ${add_const_tags(block, this.node.const_tags, '#ctx')} + } + `); + } + if (this.node.else && this.node.else.const_tags.length) { + else_get_context = block.renderer.component.get_unique_name('get_context'); + block.renderer.blocks.push(b` + function ${else_get_context}(#ctx) { + ${add_const_tags(block, this.node.const_tags, '#ctx')} + } + `); + } + if (this.else.length > 0) { return b` if (${this.node.expression.manipulate(block, '#ctx')}) { + ${if_get_context ? x`${if_get_context}(#ctx)` : null} ${this.children.map(slot => slot.render_slot_template_definition(block))} } else { + ${else_get_context ? x`${else_get_context}(#ctx)` : null} ${this.else.map(slot => slot.render_slot_template_definition(block))} } `; @@ -67,6 +93,7 @@ export default class SlotTemplateIfBlockWrapper extends Wrapper { return b` if (${this.node.expression.manipulate(block, '#ctx')}) { + ${if_get_context ? x`${if_get_context}(#ctx)` : null} ${this.children.map(slot => slot.render_slot_template_definition(block))} } `; diff --git a/src/compiler/compile/render_ssr/handlers/SlotTemplateIfBlock.ts b/src/compiler/compile/render_ssr/handlers/SlotTemplateIfBlock.ts index c0f0b458aa..7e9ddcd2ca 100644 --- a/src/compiler/compile/render_ssr/handlers/SlotTemplateIfBlock.ts +++ b/src/compiler/compile/render_ssr/handlers/SlotTemplateIfBlock.ts @@ -1,7 +1,7 @@ import Renderer, { RenderOptions } from '../Renderer'; import { b } from 'code-red'; import SlotTemplateIfBlock from '../../nodes/SlotTemplateIfBlock'; -import { flatten } from '../../../utils/flatten'; +import { get_const_tags } from './shared/get_const_tags'; export default function (node: SlotTemplateIfBlock, renderer: Renderer, options: RenderOptions) { const if_slot_scopes = []; @@ -16,14 +16,17 @@ export default function (node: SlotTemplateIfBlock, renderer: Renderer, options: })); options.slot_scopes.push(b` if (${node.expression.node}) { + ${get_const_tags(node.const_tags)} ${if_slot_scopes} } else { + ${get_const_tags(node.else.const_tags)} ${else_slot_scopes} } `); } else { options.slot_scopes.push(b`if (${node.expression.node}) { - ${flatten(if_slot_scopes)} + ${get_const_tags(node.const_tags)} + ${if_slot_scopes} }`); } } diff --git a/test/runtime/samples/component-dynamic-slot-7/Foo.svelte b/test/runtime/samples/component-dynamic-slot-7/Foo.svelte new file mode 100644 index 0000000000..4dc85932e2 --- /dev/null +++ b/test/runtime/samples/component-dynamic-slot-7/Foo.svelte @@ -0,0 +1,7 @@ + + + +
+ \ No newline at end of file diff --git a/test/runtime/samples/component-dynamic-slot-7/_config.js b/test/runtime/samples/component-dynamic-slot-7/_config.js new file mode 100644 index 0000000000..6d35733552 --- /dev/null +++ b/test/runtime/samples/component-dynamic-slot-7/_config.js @@ -0,0 +1,55 @@ +export default { + html: ` + 3 ~ 15 +
+
sum: 7 product: 2 all: 14
+ `, + test({ assert, component, target }) { + component.top = { a: 5, b: 6 }; + assert.htmlEqual(target.innerHTML, ` + 11 ~ 55 +
+
sum: 7 product: 30 all: 42
+ `); + + component.main_constant = 3; + assert.htmlEqual(target.innerHTML, ` + 11 ~ 165 +
+
sum: 7 product: 90 all: 102
+ `); + + component.top = false; + assert.htmlEqual(target.innerHTML, ` +
+
sum: 7 all: 12
+ `); + + component.bottom = false; + assert.htmlEqual(target.innerHTML, ` +
+
35
+ `); + + component.top = { a: 2, b: 3 }; + assert.htmlEqual(target.innerHTML, ` + 5 ~ 75 +
+
35
+ `); + + component.main_constant = 1; + assert.htmlEqual(target.innerHTML, ` + 5 ~ 25 +
+
15
+ `); + + component.foo_constant = 3; + assert.htmlEqual(target.innerHTML, ` + 5 ~ 15 +
+
9
+ `); + } +}; diff --git a/test/runtime/samples/component-dynamic-slot-7/main.svelte b/test/runtime/samples/component-dynamic-slot-7/main.svelte new file mode 100644 index 0000000000..6a36f7ec31 --- /dev/null +++ b/test/runtime/samples/component-dynamic-slot-7/main.svelte @@ -0,0 +1,38 @@ + + + + {#if top} + {@const sum = top.a + top.b} + + {@const csum = sum * constant * main_constant} + {sum} ~ {csum} + + {/if} + {#if bottom} + {@const sum = bottom.a + bottom.b} + {#if top} + {@const product = top.a * top.b * main_constant} + + {@const all = constant + product + sum} +
sum: {sum} product: {product} all: {all}
+
+ {:else} + + {@const all = constant + sum} +
sum: {sum} all: {all}
+
+ {/if} + {:else} + {@const product = foo_constant + foo_constant} + + {@const all = constant + product * main_constant} +
{all}
+
+ {/if} +
diff --git a/test/runtime/samples/component-dynamic-slot-8/Foo.svelte b/test/runtime/samples/component-dynamic-slot-8/Foo.svelte new file mode 100644 index 0000000000..043fbb4a54 --- /dev/null +++ b/test/runtime/samples/component-dynamic-slot-8/Foo.svelte @@ -0,0 +1,11 @@ + + +
+ fallback {value * 2} + {#if $$slots.alert} +
+ + {/if} +
diff --git a/test/runtime/samples/component-dynamic-slot-8/_config.js b/test/runtime/samples/component-dynamic-slot-8/_config.js new file mode 100644 index 0000000000..19596f4b65 --- /dev/null +++ b/test/runtime/samples/component-dynamic-slot-8/_config.js @@ -0,0 +1,76 @@ +export default { + html: ` +
+ 2 ~ 3 +
+ 3 ~ 5 +
+ 5 ~ 8 +
+ #2 > 5 +
+
+
+ `, + test({ assert, component, target }) { + component.array = [3, 5, 8]; + assert.htmlEqual(target.innerHTML, ` +
+ 2 ~ 5 +
+ 5 ~ 10 +
+ 10 ~ 18 +
+ #2 > 5 +
+
+ #1 > 5 +
+
+ `); + + component.value = 8; + assert.htmlEqual(target.innerHTML, ` +
+ 8 ~ 11 +
+ 11 ~ 16 +
+ 16 ~ 24 +
+ #2 > 5 +
+
+ #1 > 5 +
+
+ #0 > 5 +
+ `); + + component.array = [1, 3]; + assert.htmlEqual(target.innerHTML, ` +
+ 8 ~ 9 +
+ 9 ~ 12 +
+ fallback 24 +
+
+ #1 > 5 +
+
+ #0 > 5 +
+ `); + + component.array = []; + assert.htmlEqual(target.innerHTML, ` +
+ fallback 16 +
+ `); + } +}; diff --git a/test/runtime/samples/component-dynamic-slot-8/main.svelte b/test/runtime/samples/component-dynamic-slot-8/main.svelte new file mode 100644 index 0000000000..55422b07f8 --- /dev/null +++ b/test/runtime/samples/component-dynamic-slot-8/main.svelte @@ -0,0 +1,39 @@ + + + + {#if array[0]} + {@const sum = array[0] + value} + {#if sum > 5} + #0 > 5 + {/if} + + {value} ~ {sum} + + {#if array[1]} + {@const sum1 = array[1] + sum} + {#if sum1 > 5} + #1 > 5 + {/if} + + {value} ~ {sum1} + + {#if array[2]} + {@const sum2 = array[2] + sum1} + {#if sum2 > 5} + #2 > 5 + {/if} + + {value} ~ {sum2} + + {/if} + + + {/if} + + + {/if} + diff --git a/test/runtime/samples/component-dynamic-slot-9/Foo.svelte b/test/runtime/samples/component-dynamic-slot-9/Foo.svelte new file mode 100644 index 0000000000..043fbb4a54 --- /dev/null +++ b/test/runtime/samples/component-dynamic-slot-9/Foo.svelte @@ -0,0 +1,11 @@ + + +
+ fallback {value * 2} + {#if $$slots.alert} +
+ + {/if} +
diff --git a/test/runtime/samples/component-dynamic-slot-9/_config.js b/test/runtime/samples/component-dynamic-slot-9/_config.js new file mode 100644 index 0000000000..19596f4b65 --- /dev/null +++ b/test/runtime/samples/component-dynamic-slot-9/_config.js @@ -0,0 +1,76 @@ +export default { + html: ` +
+ 2 ~ 3 +
+ 3 ~ 5 +
+ 5 ~ 8 +
+ #2 > 5 +
+
+
+ `, + test({ assert, component, target }) { + component.array = [3, 5, 8]; + assert.htmlEqual(target.innerHTML, ` +
+ 2 ~ 5 +
+ 5 ~ 10 +
+ 10 ~ 18 +
+ #2 > 5 +
+
+ #1 > 5 +
+
+ `); + + component.value = 8; + assert.htmlEqual(target.innerHTML, ` +
+ 8 ~ 11 +
+ 11 ~ 16 +
+ 16 ~ 24 +
+ #2 > 5 +
+
+ #1 > 5 +
+
+ #0 > 5 +
+ `); + + component.array = [1, 3]; + assert.htmlEqual(target.innerHTML, ` +
+ 8 ~ 9 +
+ 9 ~ 12 +
+ fallback 24 +
+
+ #1 > 5 +
+
+ #0 > 5 +
+ `); + + component.array = []; + assert.htmlEqual(target.innerHTML, ` +
+ fallback 16 +
+ `); + } +}; diff --git a/test/runtime/samples/component-dynamic-slot-9/main.svelte b/test/runtime/samples/component-dynamic-slot-9/main.svelte new file mode 100644 index 0000000000..815a2af6a8 --- /dev/null +++ b/test/runtime/samples/component-dynamic-slot-9/main.svelte @@ -0,0 +1,39 @@ + + + + {#if array[0]} + {@const sum = array[0] + value} + {#if sum > 5} + #0 > 5 + {/if} + + {value} ~ {sum} + + {#if array[1]} + {@const sum = array[0] + array[1] + value} + {#if sum > 5} + #1 > 5 + {/if} + + {value} ~ {sum} + + {#if array[2]} + {@const sum = array[1] + array[2] + value} + {#if sum > 5} + #2 > 5 + {/if} + + {value} ~ {sum} + + {/if} + + + {/if} + + + {/if} + diff --git a/test/runtime/samples/component-slot-nested-in-slot/_config.js b/test/runtime/samples/component-slot-nested-in-slot/_config.js index 91a1b79c42..56a9ef2668 100644 --- a/test/runtime/samples/component-slot-nested-in-slot/_config.js +++ b/test/runtime/samples/component-slot-nested-in-slot/_config.js @@ -1,5 +1,4 @@ export default { - solo:true, html: `

one: 1 two: 2

`, diff --git a/test/runtime/samples/component-slot-slot/_config.js b/test/runtime/samples/component-slot-slot/_config.js index 09311852cc..32af774fac 100644 --- a/test/runtime/samples/component-slot-slot/_config.js +++ b/test/runtime/samples/component-slot-slot/_config.js @@ -1,4 +1,3 @@ export default { - solo:true, html: '
lol
' };