From 5e30e90e70b529aec1bd6799f4d66d42a115e12d Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sat, 26 Jan 2019 17:29:16 -0500 Subject: [PATCH] more gnarly slot stuff --- src/compile/render-dom/wrappers/Slot.ts | 50 +++++++++++++++++-- src/compile/render-ssr/handlers/AwaitBlock.ts | 2 +- src/compile/render-ssr/handlers/EachBlock.ts | 2 +- src/compile/render-ssr/handlers/Element.ts | 4 +- src/compile/render-ssr/handlers/HtmlTag.ts | 2 +- src/compile/render-ssr/handlers/IfBlock.ts | 2 +- .../render-ssr/handlers/InlineComponent.ts | 2 +- src/compile/render-ssr/handlers/Slot.ts | 19 ++----- src/compile/render-ssr/handlers/Tag.ts | 2 +- src/internal/utils.js | 10 ++-- src/utils/get_slot_data.ts | 18 +++++++ .../render-ssr/utils.ts => utils/snip.ts} | 0 .../shared => utils}/stringify_attribute.ts | 8 +-- .../samples/component-slot-let-c/Nested.html | 6 +++ .../samples/component-slot-let-c/_config.js | 18 +++++++ .../samples/component-slot-let-c/main.html | 7 +++ 16 files changed, 115 insertions(+), 37 deletions(-) create mode 100644 src/utils/get_slot_data.ts rename src/{compile/render-ssr/utils.ts => utils/snip.ts} (100%) rename src/{compile/render-ssr/handlers/shared => utils}/stringify_attribute.ts (58%) create mode 100644 test/runtime/samples/component-slot-let-c/Nested.html create mode 100644 test/runtime/samples/component-slot-let-c/_config.js create mode 100644 test/runtime/samples/component-slot-let-c/main.html diff --git a/src/compile/render-dom/wrappers/Slot.ts b/src/compile/render-dom/wrappers/Slot.ts index 42b028af01..5c279e5e26 100644 --- a/src/compile/render-dom/wrappers/Slot.ts +++ b/src/compile/render-dom/wrappers/Slot.ts @@ -6,6 +6,9 @@ import FragmentWrapper from './Fragment'; import deindent from '../../../utils/deindent'; import sanitize from '../../../utils/sanitize'; import addToSet from '../../../utils/addToSet'; +import get_slot_data from '../../../utils/get_slot_data'; +import stringifyProps from '../../../utils/stringifyProps'; +import Expression from '../../nodes/shared/Expression'; export default class SlotWrapper extends Wrapper { node: Slot; @@ -51,11 +54,50 @@ export default class SlotWrapper extends Wrapper { const slot_name = this.node.getStaticAttributeValue('name') || 'default'; renderer.slots.add(slot_name); + let get_slot_changes; + let get_slot_context; + + const attributes = this.node.attributes.filter(attribute => attribute.name !== 'name'); + + if (attributes.length > 0) { + get_slot_changes = renderer.component.getUniqueName(`get_${slot_name}_slot_changes`); + get_slot_context = renderer.component.getUniqueName(`get_${slot_name}_slot_context`); + + const context_props = get_slot_data(attributes); + const changes_props = []; + + const dependencies = new Set(); + + attributes.forEach(attribute => { + attribute.chunks.forEach(chunk => { + if ((chunk).dependencies) { + addToSet(dependencies, (chunk).dependencies); + addToSet(dependencies, (chunk).contextual_dependencies); + } + }); + + if (attribute.dependencies.size > 0) { + changes_props.push(`${attribute.name}: ${[...attribute.dependencies].join(' || ')}`) + } + }); + + const arg = dependencies.size > 0 ? `{ ${[...dependencies].join(', ')} }` : '{}'; + + renderer.blocks.push(deindent` + const ${get_slot_changes} = (${arg}) => (${stringifyProps(changes_props)}); + const ${get_slot_context} = (${arg}) => (${stringifyProps(context_props)}); + `); + } else { + get_slot_context = 'null'; + } + const slot = block.getUniqueName(`${sanitize(slot_name)}_slot`); + const slot_definition = block.getUniqueName(`${sanitize(slot_name)}_slot`); - block.builders.init.addLine( - `const ${slot} = @create_slot(ctx.$$slot_${sanitize(slot_name)}, ctx);` - ); + block.builders.init.addBlock(deindent` + const ${slot_definition} = ctx.$$slot_${sanitize(slot_name)}; + const ${slot} = @create_slot(${slot_definition}, ctx, ${get_slot_context}); + `); let mountBefore = block.builders.mount.toString(); @@ -100,7 +142,7 @@ export default class SlotWrapper extends Wrapper { block.builders.update.addBlock(deindent` if (${slot} && ${update_conditions}) { - ${slot}.p(@assign(@assign({}, changed), ctx.$$scope.changed), @get_slot_context(ctx.$$slot_${sanitize(slot_name)}, ctx)); + ${slot}.p(@assign(@assign({}, ${get_slot_changes}(changed)), ctx.$$scope.changed), @get_slot_context(${slot_definition}, ctx, ${get_slot_context})); } `); diff --git a/src/compile/render-ssr/handlers/AwaitBlock.ts b/src/compile/render-ssr/handlers/AwaitBlock.ts index 05da3a4ad6..a79ff18429 100644 --- a/src/compile/render-ssr/handlers/AwaitBlock.ts +++ b/src/compile/render-ssr/handlers/AwaitBlock.ts @@ -1,6 +1,6 @@ import Renderer from '../Renderer'; import { CompileOptions } from '../../../interfaces'; -import { snip } from '../utils'; +import { snip } from '../../../utils/snip'; export default function(node, renderer: Renderer, options: CompileOptions) { renderer.append('${(function(__value) { if(@isPromise(__value)) return `'); diff --git a/src/compile/render-ssr/handlers/EachBlock.ts b/src/compile/render-ssr/handlers/EachBlock.ts index 57b0ddd1f1..a764b151aa 100644 --- a/src/compile/render-ssr/handlers/EachBlock.ts +++ b/src/compile/render-ssr/handlers/EachBlock.ts @@ -1,4 +1,4 @@ -import { snip } from '../utils'; +import { snip } from '../../../utils/snip'; export default function(node, renderer, options) { const snippet = snip(node.expression); diff --git a/src/compile/render-ssr/handlers/Element.ts b/src/compile/render-ssr/handlers/Element.ts index c7eeeb01f4..ba5bd998ab 100644 --- a/src/compile/render-ssr/handlers/Element.ts +++ b/src/compile/render-ssr/handlers/Element.ts @@ -2,8 +2,8 @@ import { quotePropIfNecessary, quoteNameIfNecessary } from '../../../utils/quote import isVoidElementName from '../../../utils/isVoidElementName'; import Attribute from '../../nodes/Attribute'; import Node from '../../nodes/shared/Node'; -import { snip } from '../utils'; -import { stringify_attribute } from './shared/stringify_attribute'; +import { snip } from '../../../utils/snip'; +import { stringify_attribute } from '../../../utils/stringify_attribute'; import { get_slot_scope } from './shared/get_slot_scope'; // source: https://gist.github.com/ArjanSchouten/0b8574a6ad7f5065a5e7 diff --git a/src/compile/render-ssr/handlers/HtmlTag.ts b/src/compile/render-ssr/handlers/HtmlTag.ts index ce7c8bf97c..4d6934d892 100644 --- a/src/compile/render-ssr/handlers/HtmlTag.ts +++ b/src/compile/render-ssr/handlers/HtmlTag.ts @@ -1,4 +1,4 @@ -import { snip } from '../utils'; +import { snip } from '../../../utils/snip'; export default function(node, renderer, options) { renderer.append('${' + snip(node.expression) + '}'); diff --git a/src/compile/render-ssr/handlers/IfBlock.ts b/src/compile/render-ssr/handlers/IfBlock.ts index fadaccef98..9063bcc988 100644 --- a/src/compile/render-ssr/handlers/IfBlock.ts +++ b/src/compile/render-ssr/handlers/IfBlock.ts @@ -1,4 +1,4 @@ -import { snip } from '../utils'; +import { snip } from '../../../utils/snip'; export default function(node, renderer, options) { const snippet = snip(node.expression); diff --git a/src/compile/render-ssr/handlers/InlineComponent.ts b/src/compile/render-ssr/handlers/InlineComponent.ts index 41cb569e93..d1776988b5 100644 --- a/src/compile/render-ssr/handlers/InlineComponent.ts +++ b/src/compile/render-ssr/handlers/InlineComponent.ts @@ -1,6 +1,6 @@ import { escape, escapeTemplate, stringify } from '../../../utils/stringify'; import { quoteNameIfNecessary } from '../../../utils/quoteIfNecessary'; -import { snip } from '../utils'; +import { snip } from '../../../utils/snip'; import Renderer from '../Renderer'; import stringifyProps from '../../../utils/stringifyProps'; import { get_slot_scope } from './shared/get_slot_scope'; diff --git a/src/compile/render-ssr/handlers/Slot.ts b/src/compile/render-ssr/handlers/Slot.ts index 04fca84538..6bf2a22cad 100644 --- a/src/compile/render-ssr/handlers/Slot.ts +++ b/src/compile/render-ssr/handlers/Slot.ts @@ -1,6 +1,5 @@ import { quotePropIfNecessary } from '../../../utils/quoteIfNecessary'; -import { snip } from '../utils'; -import { stringify_attribute } from './shared/stringify_attribute'; +import get_slot_data from '../../../utils/get_slot_data'; export default function(node, renderer, options) { const name = node.attributes.find(attribute => attribute.name === 'name'); @@ -8,21 +7,9 @@ export default function(node, renderer, options) { const slot_name = name && name.chunks[0].data || 'default'; const prop = quotePropIfNecessary(slot_name); - const slot_data = node.attributes - .filter(attribute => attribute.name !== 'name') - .map(attribute => { - const value = attribute.isTrue - ? 'true' - : attribute.chunks.length === 0 - ? '""' - : attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text' - ? snip(attribute.chunks[0]) - : stringify_attribute(attribute); + const slot_data = get_slot_data(node.attributes); - return `${attribute.name}: ${value}`; - }); - - const arg = slot_data ? `{ ${slot_data.join(', ')} }` : ''; + const arg = slot_data.length > 0 ? `{ ${slot_data.join(', ')} }` : ''; renderer.append(`\${$$slots${prop} ? $$slots${prop}(${arg}) : \``); diff --git a/src/compile/render-ssr/handlers/Tag.ts b/src/compile/render-ssr/handlers/Tag.ts index f4bebed552..2cfbafd48a 100644 --- a/src/compile/render-ssr/handlers/Tag.ts +++ b/src/compile/render-ssr/handlers/Tag.ts @@ -1,4 +1,4 @@ -import { snip } from '../utils'; +import { snip } from '../../../utils/snip'; export default function(node, renderer, options) { const snippet = snip(node.expression); diff --git a/src/internal/utils.js b/src/internal/utils.js index 1ce5f10cd3..5ac71b4fce 100644 --- a/src/internal/utils.js +++ b/src/internal/utils.js @@ -47,16 +47,16 @@ export function validate_store(store, name) { } } -export function create_slot(definition, ctx) { +export function create_slot(definition, ctx, fn) { if (definition) { - const slot_ctx = get_slot_context(definition, ctx); + const slot_ctx = get_slot_context(definition, ctx, fn); return definition[0](slot_ctx); } } -export function get_slot_context(definition, ctx) { +export function get_slot_context(definition, ctx, fn) { return definition[1] - ? assign({}, assign(ctx.$$scope.ctx, definition[1](ctx))) - : ctx.$$scope.ctx + ? assign({}, assign(ctx.$$scope.ctx, definition[1](fn ? fn(ctx) : {}))) + : ctx.$$scope.ctx; } diff --git a/src/utils/get_slot_data.ts b/src/utils/get_slot_data.ts new file mode 100644 index 0000000000..d5a5205ac9 --- /dev/null +++ b/src/utils/get_slot_data.ts @@ -0,0 +1,18 @@ +import { snip } from './snip'; +import { stringify_attribute } from './stringify_attribute'; + +export default function(attributes) { + return attributes + .filter(attribute => attribute.name !== 'name') + .map(attribute => { + const value = attribute.isTrue + ? 'true' + : attribute.chunks.length === 0 + ? '""' + : attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text' + ? snip(attribute.chunks[0]) + : stringify_attribute(attribute); + + return `${attribute.name}: ${value}`; + }); +} \ No newline at end of file diff --git a/src/compile/render-ssr/utils.ts b/src/utils/snip.ts similarity index 100% rename from src/compile/render-ssr/utils.ts rename to src/utils/snip.ts diff --git a/src/compile/render-ssr/handlers/shared/stringify_attribute.ts b/src/utils/stringify_attribute.ts similarity index 58% rename from src/compile/render-ssr/handlers/shared/stringify_attribute.ts rename to src/utils/stringify_attribute.ts index 1182d61190..933a84ed03 100644 --- a/src/compile/render-ssr/handlers/shared/stringify_attribute.ts +++ b/src/utils/stringify_attribute.ts @@ -1,7 +1,7 @@ -import Attribute from '../../../nodes/Attribute'; -import Node from '../../../nodes/shared/Node'; -import { escapeTemplate, escape } from '../../../../utils/stringify'; -import { snip } from '../../utils'; +import Attribute from '../compile/nodes/Attribute'; +import Node from '../compile/nodes/shared/Node'; +import { escapeTemplate, escape } from './stringify'; +import { snip } from './snip'; export function stringify_attribute(attribute: Attribute) { return attribute.chunks diff --git a/test/runtime/samples/component-slot-let-c/Nested.html b/test/runtime/samples/component-slot-let-c/Nested.html new file mode 100644 index 0000000000..d38a1403fb --- /dev/null +++ b/test/runtime/samples/component-slot-let-c/Nested.html @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-c/_config.js b/test/runtime/samples/component-slot-let-c/_config.js new file mode 100644 index 0000000000..fe8e68b1ac --- /dev/null +++ b/test/runtime/samples/component-slot-let-c/_config.js @@ -0,0 +1,18 @@ +export default { + html: ` + + 0 (undefined) + `, + + async test({ assert, target, window }) { + const button = target.querySelector('button'); + const click = new window.MouseEvent('click'); + + await button.dispatchEvent(click); + + assert.htmlEqual(target.innerHTML, ` + + 1 (undefined) + `); + } +}; diff --git a/test/runtime/samples/component-slot-let-c/main.html b/test/runtime/samples/component-slot-let-c/main.html new file mode 100644 index 0000000000..b834fd8790 --- /dev/null +++ b/test/runtime/samples/component-slot-let-c/main.html @@ -0,0 +1,7 @@ + + + + {c} ({count}) + \ No newline at end of file