From ae02d4720afb24af122bd18905c168a9ae60c94f Mon Sep 17 00:00:00 2001 From: Nguyen Tran Date: Tue, 25 Apr 2023 13:56:35 -0400 Subject: [PATCH] Implement dynamic slot attribute --- src/compiler/compile/nodes/Element.ts | 4 --- src/compiler/compile/nodes/SlotTemplate.ts | 18 ++++++++----- .../wrappers/InlineComponent/index.ts | 27 ++++++++++++------- .../render_dom/wrappers/SlotTemplate.ts | 5 +--- .../render_ssr/handlers/InlineComponent.ts | 4 +-- .../compile/render_ssr/handlers/Slot.ts | 2 +- .../render_ssr/handlers/SlotTemplate.ts | 3 ++- 7 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 2410904d63..9f3284cab9 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -529,10 +529,6 @@ export default class Element extends Node { } if (name === 'slot') { - if (!attribute.is_static) { - return component.error(attribute, compiler_errors.invalid_slot_attribute); - } - if (component.slot_outlets.has(name)) { return component.error(attribute, compiler_errors.duplicate_slot_attribute(name)); diff --git a/src/compiler/compile/nodes/SlotTemplate.ts b/src/compiler/compile/nodes/SlotTemplate.ts index 90299c8e39..c9bee04e37 100644 --- a/src/compiler/compile/nodes/SlotTemplate.ts +++ b/src/compiler/compile/nodes/SlotTemplate.ts @@ -16,6 +16,7 @@ export default class SlotTemplate extends Node { const_tags: ConstTag[]; slot_attribute: Attribute; slot_template_name: string = 'default'; + is_static: boolean = true; constructor( component: Component, @@ -44,14 +45,17 @@ export default class SlotTemplate extends Node { case 'Attribute': { if (node.name === 'slot') { this.slot_attribute = new Attribute(component, this, scope, node); - if (!this.slot_attribute.is_static) { - return component.error(node, compiler_errors.invalid_slot_attribute); + if (this.slot_attribute.is_static) { + const value = this.slot_attribute.get_static_value(); + if (typeof value === 'boolean') { + return component.error(node, compiler_errors.invalid_slot_attribute_value_missing); + } + this.slot_template_name = value as string; + this.is_static = true; + } else { + this.slot_template_name = component.get_unique_name('dynamic_slot_template').name; + this.is_static = false; } - const value = this.slot_attribute.get_static_value(); - if (typeof value === 'boolean') { - return component.error(node, compiler_errors.invalid_slot_attribute_value_missing); - } - this.slot_template_name = value as string; break; } throw new Error(`Invalid attribute '${node.name}' in `); diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index ab140a75c5..502690d2c5 100644 --- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts @@ -29,7 +29,8 @@ const regex_invalid_variable_identifier_characters = /[^a-zA-Z_$]/g; export default class InlineComponentWrapper extends Wrapper { var: Identifier; - slots: Map = new Map(); + slots: Map = new Map(); + staic_slot_names: Set = new Set(); node: InlineComponent; fragment: FragmentWrapper; children: Array = []; @@ -95,14 +96,20 @@ export default class InlineComponentWrapper extends Wrapper { block.add_outro(); } - set_slot(name: string, slot_definition: SlotDefinition) { - if (this.slots.has(name)) { - if (name === 'default') { - throw new Error('Found elements without slot attribute when using slot="default"'); + set_slot(slot: SlotTemplate, slot_definition: SlotDefinition) { + if (slot.is_static) { + const name = slot.slot_template_name; + if (this.staic_slot_names.has(name)) { + if (name === 'default') { + throw new Error('Found elements without slot attribute when using slot="default"'); + } + throw new Error(`Duplicate slot name "${name}" in <${this.node.name}>`); + } else { + this.staic_slot_names.add(name); } - throw new Error(`Duplicate slot name "${name}" in <${this.node.name}>`); } - this.slots.set(name, slot_definition); + + this.slots.set(slot, slot_definition); } warn_if_reactive() { @@ -167,8 +174,10 @@ export default class InlineComponentWrapper extends Wrapper { const initial_props = this.slots.size > 0 ? [ p`$$slots: { - ${Array.from(this.slots).map(([name, slot]) => { - return p`${name}: [${slot.block.name}, ${slot.get_context || null}, ${slot.get_changes || null}]`; + ${Array.from(this.slots).map(([slot_template, slot]) => { + const { is_static, slot_template_name, slot_attribute } = slot_template; + const slot_expression = is_static ? { type: 'Literal', value: slot_template_name } : slot_attribute.get_value(block); + return p`[${slot_expression}]: [${slot.block.name}, ${slot.get_context || null}, ${slot.get_changes || null}]`; })} }`, p`$$scope: { diff --git a/src/compiler/compile/render_dom/wrappers/SlotTemplate.ts b/src/compiler/compile/render_dom/wrappers/SlotTemplate.ts index a50f74fc04..e885827157 100644 --- a/src/compiler/compile/render_dom/wrappers/SlotTemplate.ts +++ b/src/compiler/compile/render_dom/wrappers/SlotTemplate.ts @@ -52,10 +52,7 @@ export default class SlotTemplateWrapper extends Wrapper { if (!seen.has(l.name.name)) lets.push(l); }); - this.parent.set_slot( - slot_template_name, - get_slot_definition(this.block, scope, lets) - ); + this.parent.set_slot(this.node, get_slot_definition(this.block, scope, lets)); this.fragment = new FragmentWrapper( renderer, diff --git a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts index 73d31940e2..445db7d9a8 100644 --- a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts +++ b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts @@ -77,9 +77,9 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend slot_scopes })); - slot_scopes.forEach(({ input, output, statements }, name) => { + slot_scopes.forEach(({ input, output, statements }, slot_exp) => { slot_fns.push( - p`${name}: (${input}) => { ${statements}; return ${output}; }` + p`[${slot_exp}]: (${input}) => { ${statements}; return ${output}; }` ); }); } diff --git a/src/compiler/compile/render_ssr/handlers/Slot.ts b/src/compiler/compile/render_ssr/handlers/Slot.ts index 137d9c8e6e..e92847f177 100644 --- a/src/compiler/compile/render_ssr/handlers/Slot.ts +++ b/src/compiler/compile/render_ssr/handlers/Slot.ts @@ -9,7 +9,7 @@ export default function(node: Slot, renderer: Renderer, options: RenderOptions & slot_scopes: Map; }) { const slot_data = get_slot_data(node.values); - const slot = node.get_static_attribute_value('slot'); + const slot = node.values.get('slot')?.get_value(null); const nearest_inline_component = node.find_nearest(/InlineComponent/); if (slot && nearest_inline_component) { diff --git a/src/compiler/compile/render_ssr/handlers/SlotTemplate.ts b/src/compiler/compile/render_ssr/handlers/SlotTemplate.ts index 09f3293301..b09ead78dd 100644 --- a/src/compiler/compile/render_ssr/handlers/SlotTemplate.ts +++ b/src/compiler/compile/render_ssr/handlers/SlotTemplate.ts @@ -29,7 +29,8 @@ export default function(node: SlotTemplate, renderer: Renderer, options: RenderO throw new Error(`Duplicate slot name "${node.slot_template_name}" in <${parent_inline_component.name}>`); } - options.slot_scopes.set(node.slot_template_name, { + const slot_expression = node.is_static ? { type: 'Literal', value: node.slot_template_name } : node.slot_attribute.get_value(null); + options.slot_scopes.set(slot_expression, { input: get_slot_scope(node.lets), output: slot_fragment_content, statements: get_const_tags(node.const_tags)