From 582e8bb5f769b660c8d2688bd417778caae55c05 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Mon, 21 Jan 2019 22:06:26 -0500 Subject: [PATCH] support aliasing and destructuring --- src/compile/nodes/Let.ts | 33 ++++++++++++------ .../wrappers/shared/get_context_merger.ts | 5 ++- src/compile/render-ssr/handlers/Element.ts | 4 +-- .../render-ssr/handlers/InlineComponent.ts | 12 +++---- .../handlers/shared/get_slot_context.ts | 6 ---- .../handlers/shared/get_slot_scope.ts | 6 ++++ .../component-slot-let-aliased/Nested.html | 5 +++ .../component-slot-let-aliased/_config.js | 24 +++++++++++++ .../component-slot-let-aliased/main.html | 9 +++++ .../Nested.html | 5 +++ .../_config.js | 34 +++++++++++++++++++ .../component-slot-let-destructured/main.html | 9 +++++ .../component-slot-each-block/errors.json | 15 -------- .../component-slot-each-block/input.html | 3 -- 14 files changed, 126 insertions(+), 44 deletions(-) delete mode 100644 src/compile/render-ssr/handlers/shared/get_slot_context.ts create mode 100644 src/compile/render-ssr/handlers/shared/get_slot_scope.ts create mode 100644 test/runtime/samples/component-slot-let-aliased/Nested.html create mode 100644 test/runtime/samples/component-slot-let-aliased/_config.js create mode 100644 test/runtime/samples/component-slot-let-aliased/main.html create mode 100644 test/runtime/samples/component-slot-let-destructured/Nested.html create mode 100644 test/runtime/samples/component-slot-let-destructured/_config.js create mode 100644 test/runtime/samples/component-slot-let-destructured/main.html delete mode 100644 test/validator/samples/component-slot-each-block/errors.json delete mode 100644 test/validator/samples/component-slot-each-block/input.html diff --git a/src/compile/nodes/Let.ts b/src/compile/nodes/Let.ts index 3d5a7935b8..d855068c16 100644 --- a/src/compile/nodes/Let.ts +++ b/src/compile/nodes/Let.ts @@ -1,27 +1,38 @@ import Node from './shared/Node'; -import Expression from './shared/Expression'; import Component from '../Component'; +import { walk } from 'estree-walker'; -class Pattern { - constructor(node) { - // TODO implement `let:foo={bar}` and `let:contact={{ name, address }}` etc - } -} +const applicable = new Set(['Identifier', 'ObjectExpression', 'ArrayExpression', 'Property']); export default class Let extends Node { type: 'Let'; name: string; - pattern: Pattern; - names: string[]; + value: string; + names: string[] = []; constructor(component: Component, parent, scope, info) { super(component, parent, scope, info); this.name = info.name; + this.value = info.expression && `[✂${info.expression.start}-${info.expression.end}✂]`; - this.pattern = info.expression && new Pattern(info.expression); + if (info.expression) { + walk(info.expression, { + enter: node => { + if (!applicable.has(node.type)) { + component.error(node, { + code: 'invalid-let', + message: `let directive value must be an identifier or an object/array pattern` + }); + } - // TODO - this.names = [this.name]; + if (node.type === 'Identifier') { + this.names.push(node.name); + } + } + }); + } else { + this.names.push(this.name); + } } } \ No newline at end of file diff --git a/src/compile/render-dom/wrappers/shared/get_context_merger.ts b/src/compile/render-dom/wrappers/shared/get_context_merger.ts index c02cb8cca3..bbd14ec87d 100644 --- a/src/compile/render-dom/wrappers/shared/get_context_merger.ts +++ b/src/compile/render-dom/wrappers/shared/get_context_merger.ts @@ -3,5 +3,8 @@ import Let from '../../../nodes/Let'; export function get_context_merger(lets: Let[]) { if (lets.length === 0) return null; - return `({ ${lets.map(l => l.name).join(', ')} }) => ({ ${lets.map(l => l.name).join(', ')} })`; + const input = lets.map(l => l.value ? `${l.name}: ${l.value}` : l.name).join(', '); + const output = lets.map(l => l.names.join(', ')).join(', '); + + return `({ ${input} }) => ({ ${output} })`; } \ No newline at end of file diff --git a/src/compile/render-ssr/handlers/Element.ts b/src/compile/render-ssr/handlers/Element.ts index 49c3132e31..c7eeeb01f4 100644 --- a/src/compile/render-ssr/handlers/Element.ts +++ b/src/compile/render-ssr/handlers/Element.ts @@ -4,7 +4,7 @@ import Attribute from '../../nodes/Attribute'; import Node from '../../nodes/shared/Node'; import { snip } from '../utils'; import { stringify_attribute } from './shared/stringify_attribute'; -import { get_slot_context } from './shared/get_slot_context'; +import { get_slot_scope } from './shared/get_slot_scope'; // source: https://gist.github.com/ArjanSchouten/0b8574a6ad7f5065a5e7 const boolean_attributes = new Set([ @@ -59,7 +59,7 @@ export default function(node, renderer, options) { target.slotStack.push(slotName); target.slots[slotName] = ''; - options.slot_contexts.set(slotName, get_slot_context(node.lets)); + options.slot_scopes.set(slotName, get_slot_scope(node.lets)); } const classExpr = node.classes.map((classDir: Class) => { diff --git a/src/compile/render-ssr/handlers/InlineComponent.ts b/src/compile/render-ssr/handlers/InlineComponent.ts index 83e08a1511..41cb569e93 100644 --- a/src/compile/render-ssr/handlers/InlineComponent.ts +++ b/src/compile/render-ssr/handlers/InlineComponent.ts @@ -3,7 +3,7 @@ import { quoteNameIfNecessary } from '../../../utils/quoteIfNecessary'; import { snip } from '../utils'; import Renderer from '../Renderer'; import stringifyProps from '../../../utils/stringifyProps'; -import { get_slot_context } from './shared/get_slot_context'; +import { get_slot_scope } from './shared/get_slot_scope'; type AppendTarget = any; // TODO @@ -90,18 +90,18 @@ export default function(node, renderer: Renderer, options) { renderer.targets.push(target); - const slot_contexts = new Map(); - slot_contexts.set('default', get_slot_context(node.lets)); + const slot_scopes = new Map(); + slot_scopes.set('default', get_slot_scope(node.lets)); renderer.render(node.children, Object.assign({}, options, { - slot_contexts + slot_scopes })); Object.keys(target.slots).forEach(name => { - const slot_context = slot_contexts.get(name); + const slot_scope = slot_scopes.get(name); slot_fns.push( - `${quoteNameIfNecessary(name)}: (${slot_context}) => \`${target.slots[name]}\`` + `${quoteNameIfNecessary(name)}: (${slot_scope}) => \`${target.slots[name]}\`` ); }); diff --git a/src/compile/render-ssr/handlers/shared/get_slot_context.ts b/src/compile/render-ssr/handlers/shared/get_slot_context.ts deleted file mode 100644 index 5f4e4cfe69..0000000000 --- a/src/compile/render-ssr/handlers/shared/get_slot_context.ts +++ /dev/null @@ -1,6 +0,0 @@ -import Let from '../../../nodes/Let'; - -export function get_slot_context(lets: Let[]) { - if (lets.length === 0) return ''; - return `{ ${lets.map(l => `${l.name}: ${l.name}`)} }`; // TODO support aliased/destructured lets -} \ No newline at end of file diff --git a/src/compile/render-ssr/handlers/shared/get_slot_scope.ts b/src/compile/render-ssr/handlers/shared/get_slot_scope.ts new file mode 100644 index 0000000000..52aa70f015 --- /dev/null +++ b/src/compile/render-ssr/handlers/shared/get_slot_scope.ts @@ -0,0 +1,6 @@ +import Let from '../../../nodes/Let'; + +export function get_slot_scope(lets: Let[]) { + if (lets.length === 0) return ''; + return `{ ${lets.map(l => l.value ? `${l.name}: ${l.value}` : l.name).join(', ')} }`; +} \ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-aliased/Nested.html b/test/runtime/samples/component-slot-let-aliased/Nested.html new file mode 100644 index 0000000000..38c6ed6cbe --- /dev/null +++ b/test/runtime/samples/component-slot-let-aliased/Nested.html @@ -0,0 +1,5 @@ +
+ {#each things as thing} + + {/each} +
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-aliased/_config.js b/test/runtime/samples/component-slot-let-aliased/_config.js new file mode 100644 index 0000000000..d66f613bb4 --- /dev/null +++ b/test/runtime/samples/component-slot-let-aliased/_config.js @@ -0,0 +1,24 @@ +export default { + props: { + things: [1, 2, 3] + }, + + html: ` +
+ 1 + 2 + 3 +
`, + + test({ assert, component, target }) { + component.things = [1, 2, 3, 4]; + assert.htmlEqual(target.innerHTML, ` +
+ 1 + 2 + 3 + 4 +
+ `); + } +}; diff --git a/test/runtime/samples/component-slot-let-aliased/main.html b/test/runtime/samples/component-slot-let-aliased/main.html new file mode 100644 index 0000000000..1acb7fbcdc --- /dev/null +++ b/test/runtime/samples/component-slot-let-aliased/main.html @@ -0,0 +1,9 @@ + + + + {x} + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-destructured/Nested.html b/test/runtime/samples/component-slot-let-destructured/Nested.html new file mode 100644 index 0000000000..38c6ed6cbe --- /dev/null +++ b/test/runtime/samples/component-slot-let-destructured/Nested.html @@ -0,0 +1,5 @@ +
+ {#each things as thing} + + {/each} +
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-destructured/_config.js b/test/runtime/samples/component-slot-let-destructured/_config.js new file mode 100644 index 0000000000..d7aaae2d77 --- /dev/null +++ b/test/runtime/samples/component-slot-let-destructured/_config.js @@ -0,0 +1,34 @@ +export default { + props: { + things: [ + { num: 1 }, + { num: 2 }, + { num: 3 } + ] + }, + + html: ` +
+ 1 + 2 + 3 +
`, + + test({ assert, component, target }) { + component.things = [ + { num: 1 }, + { num: 2 }, + { num: 3 }, + { num: 4 } + ]; + + assert.htmlEqual(target.innerHTML, ` +
+ 1 + 2 + 3 + 4 +
+ `); + } +}; diff --git a/test/runtime/samples/component-slot-let-destructured/main.html b/test/runtime/samples/component-slot-let-destructured/main.html new file mode 100644 index 0000000000..488fd463de --- /dev/null +++ b/test/runtime/samples/component-slot-let-destructured/main.html @@ -0,0 +1,9 @@ + + + + {num} + \ No newline at end of file diff --git a/test/validator/samples/component-slot-each-block/errors.json b/test/validator/samples/component-slot-each-block/errors.json deleted file mode 100644 index 97f88e4e60..0000000000 --- a/test/validator/samples/component-slot-each-block/errors.json +++ /dev/null @@ -1,15 +0,0 @@ -[{ - "code": "invalid-slot-placement", - "message": " cannot be a child of an each-block", - "start": { - "line": 2, - "column": 1, - "character": 25 - }, - "end": { - "line": 2, - "column": 1, - "character": 25 - }, - "pos": 25 -}] \ No newline at end of file diff --git a/test/validator/samples/component-slot-each-block/input.html b/test/validator/samples/component-slot-each-block/input.html deleted file mode 100644 index ddc27a7d0d..0000000000 --- a/test/validator/samples/component-slot-each-block/input.html +++ /dev/null @@ -1,3 +0,0 @@ -{#each things as thing} - -{/each} \ No newline at end of file