Merge pull request #806 from sveltejs/gh-801

stack up append targets so that slotted content in nested components works in SSR mode
pull/807/head
Rich Harris 7 years ago committed by GitHub
commit 865e84b856

@ -6,13 +6,13 @@ import preprocess from './preprocess';
import visit from './visit'; import visit from './visit';
import { removeNode, removeObjectKey } from '../../utils/removeNode'; import { removeNode, removeObjectKey } from '../../utils/removeNode';
import { Parsed, Node, CompileOptions } from '../../interfaces'; import { Parsed, Node, CompileOptions } from '../../interfaces';
import { AppendTarget } from './interfaces';
import { stringify } from '../../utils/stringify'; import { stringify } from '../../utils/stringify';
export class SsrGenerator extends Generator { export class SsrGenerator extends Generator {
bindings: string[]; bindings: string[];
renderCode: string; renderCode: string;
appendTargets: Record<string, string> | null; appendTargets: AppendTarget[];
appendTarget: string | null;
constructor( constructor(
parsed: Parsed, parsed: Parsed,
@ -24,7 +24,7 @@ export class SsrGenerator extends Generator {
super(parsed, source, name, stylesheet, options); super(parsed, source, name, stylesheet, options);
this.bindings = []; this.bindings = [];
this.renderCode = ''; this.renderCode = '';
this.appendTargets = null; this.appendTargets = [];
// in an SSR context, we don't need to include events, methods, oncreate or ondestroy // in an SSR context, we don't need to include events, methods, oncreate or ondestroy
const { templateProperties, defaultExport } = this; const { templateProperties, defaultExport } = this;
@ -60,24 +60,14 @@ export class SsrGenerator extends Generator {
} }
append(code: string) { append(code: string) {
if (this.appendTarget) { if (this.appendTargets.length) {
this.appendTargets[this.appendTarget] += code; const appendTarget = this.appendTargets[this.appendTargets.length - 1];
const slotName = appendTarget.slotStack[appendTarget.slotStack.length - 1];
appendTarget.slots[slotName] += code;
} else { } else {
this.renderCode += code; 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( export default function ssr(

@ -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<string, string>;
slotStack: string[]
}

@ -2,6 +2,7 @@ import flattenReference from '../../../utils/flattenReference';
import visit from '../visit'; import visit from '../visit';
import { SsrGenerator } from '../index'; import { SsrGenerator } from '../index';
import Block from '../Block'; import Block from '../Block';
import { AppendTarget } from '../interfaces';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import getObject from '../../../utils/getObject'; import getObject from '../../../utils/getObject';
import getTailSnippet from '../../../utils/getTailSnippet'; import getTailSnippet from '../../../utils/getTailSnippet';
@ -80,19 +81,24 @@ export default function visitComponent(
let open = `\${${expression}.render({${props}}`; let open = `\${${expression}.render({${props}}`;
if (node.children.length) { if (node.children.length) {
generator.appendTargets = {}; const appendTarget: AppendTarget = {
generator.setAppendTarget('default'); slots: { default: '' },
slotStack: ['default']
};
generator.appendTargets.push(appendTarget);
node.children.forEach((child: Node) => { node.children.forEach((child: Node) => {
visit(generator, block, child); visit(generator, block, child);
}); });
const slotted = Object.keys(generator.appendTargets) const slotted = Object.keys(appendTarget.slots)
.map(name => `${name}: () => \`${generator.appendTargets[name]}\``) .map(name => `${name}: () => \`${appendTarget.slots[name]}\``)
.join(', '); .join(', ');
open += `, { slotted: { ${slotted} } }`; open += `, { slotted: { ${slotted} } }`;
generator.setAppendTarget(null);
generator.appendTargets.pop();
} }
generator.append(open); generator.append(open);

@ -49,7 +49,10 @@ export default function visitElement(
const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot'); const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot');
if (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) => { node.attributes.forEach((attribute: Node) => {

@ -0,0 +1,3 @@
<div class='inner'>
<slot></slot>
</div>

@ -0,0 +1,3 @@
<div class='outer'>
<slot></slot>
</div>

@ -0,0 +1,6 @@
export default {
html: `
<div class='outer'>
<div class='inner'>foo</div>
</div>`
};

@ -0,0 +1,12 @@
<Outer>
<Inner>foo</Inner>
</Outer>
<script>
import Outer from './Outer.html';
import Inner from './Inner.html';
export default {
components: { Outer, Inner }
};
</script>
Loading…
Cancel
Save