From a68f7e103f95ea25f79bb0ce7c35630eef62bdee Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 31 Aug 2017 11:10:45 -0400 Subject: [PATCH] stack up append targets so that slotted content in nested components works in SSR mode (fixes #801) --- src/generators/server-side-rendering/index.ts | 24 ++++++------------- .../server-side-rendering/interfaces.ts | 15 ++++++++++++ .../visitors/Component.ts | 16 +++++++++---- .../server-side-rendering/visitors/Element.ts | 5 +++- .../Inner.html | 3 +++ .../Outer.html | 3 +++ .../_config.js | 6 +++++ .../component-slot-nested-component/main.html | 12 ++++++++++ 8 files changed, 61 insertions(+), 23 deletions(-) create mode 100644 src/generators/server-side-rendering/interfaces.ts create mode 100644 test/runtime/samples/component-slot-nested-component/Inner.html create mode 100644 test/runtime/samples/component-slot-nested-component/Outer.html create mode 100644 test/runtime/samples/component-slot-nested-component/_config.js create mode 100644 test/runtime/samples/component-slot-nested-component/main.html diff --git a/src/generators/server-side-rendering/index.ts b/src/generators/server-side-rendering/index.ts index 16492fa386..a7cc04bedc 100644 --- a/src/generators/server-side-rendering/index.ts +++ b/src/generators/server-side-rendering/index.ts @@ -6,13 +6,13 @@ import preprocess from './preprocess'; import visit from './visit'; import { removeNode, removeObjectKey } from '../../utils/removeNode'; import { Parsed, Node, CompileOptions } from '../../interfaces'; +import { AppendTarget } from './interfaces'; import { stringify } from '../../utils/stringify'; export class SsrGenerator extends Generator { bindings: string[]; renderCode: string; - appendTargets: Record | null; - appendTarget: string | null; + appendTargets: AppendTarget[]; constructor( parsed: Parsed, @@ -24,7 +24,7 @@ export class SsrGenerator extends Generator { super(parsed, source, name, stylesheet, options); this.bindings = []; this.renderCode = ''; - this.appendTargets = null; + this.appendTargets = []; // in an SSR context, we don't need to include events, methods, oncreate or ondestroy const { templateProperties, defaultExport } = this; @@ -60,24 +60,14 @@ export class SsrGenerator extends Generator { } append(code: string) { - if (this.appendTarget) { - this.appendTargets[this.appendTarget] += code; + if (this.appendTargets.length) { + const appendTarget = this.appendTargets[this.appendTargets.length - 1]; + const slotName = appendTarget.slotStack[appendTarget.slotStack.length - 1]; + appendTarget.slots[slotName] += code; } else { this.renderCode += code; } } - - removeAppendTarget() { - this.appendTarget = this.appendTargets = null; - } - - setAppendTarget(name: string) { - if (!this.appendTargets[name]) { - this.appendTargets[name] = ''; - } - - this.appendTarget = name; - } } export default function ssr( diff --git a/src/generators/server-side-rendering/interfaces.ts b/src/generators/server-side-rendering/interfaces.ts new file mode 100644 index 0000000000..127d8bf34c --- /dev/null +++ b/src/generators/server-side-rendering/interfaces.ts @@ -0,0 +1,15 @@ +import { SsrGenerator } from './index'; +import Block from './Block'; +import { Node } from '../../interfaces'; + +export type Visitor = ( + generator: SsrGenerator, + block: Block, + node: Node, + componentStack: Node[] +) => void; + +export interface AppendTarget { + slots: Record; + slotStack: string[] +} \ No newline at end of file diff --git a/src/generators/server-side-rendering/visitors/Component.ts b/src/generators/server-side-rendering/visitors/Component.ts index 4c476055fa..7af91a0aad 100644 --- a/src/generators/server-side-rendering/visitors/Component.ts +++ b/src/generators/server-side-rendering/visitors/Component.ts @@ -2,6 +2,7 @@ import flattenReference from '../../../utils/flattenReference'; import visit from '../visit'; import { SsrGenerator } from '../index'; import Block from '../Block'; +import { AppendTarget } from '../interfaces'; import { Node } from '../../../interfaces'; import getObject from '../../../utils/getObject'; import getTailSnippet from '../../../utils/getTailSnippet'; @@ -80,19 +81,24 @@ export default function visitComponent( let open = `\${${expression}.render({${props}}`; if (node.children.length) { - generator.appendTargets = {}; - generator.setAppendTarget('default'); + const appendTarget: AppendTarget = { + slots: { default: '' }, + slotStack: ['default'] + }; + + generator.appendTargets.push(appendTarget); node.children.forEach((child: Node) => { visit(generator, block, child); }); - const slotted = Object.keys(generator.appendTargets) - .map(name => `${name}: () => \`${generator.appendTargets[name]}\``) + const slotted = Object.keys(appendTarget.slots) + .map(name => `${name}: () => \`${appendTarget.slots[name]}\``) .join(', '); open += `, { slotted: { ${slotted} } }`; - generator.setAppendTarget(null); + + generator.appendTargets.pop(); } generator.append(open); diff --git a/src/generators/server-side-rendering/visitors/Element.ts b/src/generators/server-side-rendering/visitors/Element.ts index 94af2bed82..3a1c070f26 100644 --- a/src/generators/server-side-rendering/visitors/Element.ts +++ b/src/generators/server-side-rendering/visitors/Element.ts @@ -49,7 +49,10 @@ export default function visitElement( const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot'); if (slot) { - generator.setAppendTarget(slot.value[0].data); + const slotName = slot.value[0].data; + const appendTarget = generator.appendTargets[generator.appendTargets.length - 1]; + appendTarget.slotStack.push(slotName); + appendTarget.slots[slotName] = ''; } node.attributes.forEach((attribute: Node) => { diff --git a/test/runtime/samples/component-slot-nested-component/Inner.html b/test/runtime/samples/component-slot-nested-component/Inner.html new file mode 100644 index 0000000000..4082391c60 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-component/Inner.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-nested-component/Outer.html b/test/runtime/samples/component-slot-nested-component/Outer.html new file mode 100644 index 0000000000..85e4153498 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-component/Outer.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-nested-component/_config.js b/test/runtime/samples/component-slot-nested-component/_config.js new file mode 100644 index 0000000000..922b96ae36 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-component/_config.js @@ -0,0 +1,6 @@ +export default { + html: ` +
+
foo
+
` +}; diff --git a/test/runtime/samples/component-slot-nested-component/main.html b/test/runtime/samples/component-slot-nested-component/main.html new file mode 100644 index 0000000000..2d094056c9 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-component/main.html @@ -0,0 +1,12 @@ + + foo + + + \ No newline at end of file