diff --git a/src/compiler/compile/render_ssr/Renderer.ts b/src/compiler/compile/render_ssr/Renderer.ts index ff91718437..b0c1226564 100644 --- a/src/compiler/compile/render_ssr/Renderer.ts +++ b/src/compiler/compile/render_ssr/Renderer.ts @@ -78,7 +78,7 @@ export default class Renderer { }); this.literal.expressions.push(node); - this.current = { value: '' }; + this.current.value = ''; } push() { diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index 66bf2763f9..f8f281de0a 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -66,22 +66,20 @@ export default function(node: Element, renderer: Renderer, options: RenderOption const slot = node.get_static_attribute_value('slot'); const nearest_inline_component = node.find_nearest(/InlineComponent/); - if (slot && nearest_inline_component) { - const slot = node.attributes.find((attribute) => attribute.name === 'slot'); - const slot_name = (slot.chunks[0] as Text).data; - const target = renderer.targets[renderer.targets.length - 1]; - target.slot_stack.push(slot_name); - target.slots[slot_name] = ''; - const lets = node.lets; - const seen = new Set(lets.map(l => l.name.name)); + // if (slot && nearest_inline_component) { + // const slot = node.attributes.find((attribute) => attribute.name === 'slot'); + // const slot_name = (slot.chunks[0] as Text).data; - nearest_inline_component.lets.forEach(l => { - if (!seen.has(l.name.name)) lets.push(l); - }); + // const lets = node.lets; + // const seen = new Set(lets.map(l => l.name.name)); - options.slot_scopes.set(slot_name, get_slot_scope(node.lets)); - } + // nearest_inline_component.lets.forEach(l => { + // if (!seen.has(l.name.name)) lets.push(l); + // }); + + // options.slot_scopes.set(slot_name, get_slot_scope(node.lets)); + // } const class_expression = node.classes.map((class_directive: Class) => { const { expression, name } = class_directive; @@ -189,6 +187,21 @@ export default function(node: Element, renderer: Renderer, options: RenderOption } else { renderer.add_expression(node_contents); } + } else if (slot && nearest_inline_component) { + renderer.push(); + renderer.render(node.children, options); + + const lets = node.lets; + const seen = new Set(lets.map(l => l.name.name)); + + nearest_inline_component.lets.forEach(l => { + if (!seen.has(l.name.name)) lets.push(l); + }); + + options.slot_scopes.set(slot, { + input: get_slot_scope(node.lets), + output: renderer.pop() + }); } else { renderer.render(node.children, options); } diff --git a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts index 9ca6a82b38..78ffe13f97 100644 --- a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts +++ b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts @@ -36,17 +36,16 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend let props; if (uses_spread) { - props = `@_Object.assign(${ + props = x`@_Object.assign(${ node.attributes .map(attribute => { if (attribute.is_spread) { - return snip(attribute.expression); + return attribute.expression.node; } else { - return `{ ${quote_name_if_necessary(attribute.name)}: ${get_prop_value(attribute)} }`; + return x`{ ${attribute.name}: ${get_prop_value(attribute)} }`; } }) .concat(binding_props.map(p => `{ ${p} }`)) - .join(', ') })`; } else { props = x`{ @@ -61,7 +60,7 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend const expression = ( node.name === 'svelte:self' - ? '__svelte:self__' // TODO conflict-proof this + ? options.name : node.name === 'svelte:component' ? x`(${node.expression.node}) || @missing_component` : node.name diff --git a/src/compiler/compile/render_ssr/index.ts b/src/compiler/compile/render_ssr/index.ts index fba0b2577c..f979df1b7b 100644 --- a/src/compiler/compile/render_ssr/index.ts +++ b/src/compiler/compile/render_ssr/index.ts @@ -5,6 +5,7 @@ import { stringify, string_literal } from '../utils/stringify'; import Renderer from './Renderer'; import { INode as TemplateNode } from '../nodes/interfaces'; // TODO import Text from '../nodes/Text'; +import { extract_names } from '../utils/scope'; export default function ssr( component: Component, @@ -32,14 +33,15 @@ export default function ssr( .map(({ name }) => { const store_name = name.slice(1); const store = component.var_lookup.get(store_name); - if (store && store.hoistable) return; + if (store && store.hoistable) return null; - const assignment = `${name} = @get_store_value(${store_name});`; + const assignment = b`${name} = @get_store_value(${store_name});`; return component.compile_options.dev - ? `@validate_store(${store_name}, '${store_name}'); ${assignment}` + ? b`@validate_store(${store_name}, '${store_name}'); ${assignment}` : assignment; - }); + }) + .filter(Boolean); // TODO remove this, just use component.vars everywhere const props = component.vars.filter(variable => !variable.module && variable.export_name); @@ -67,33 +69,35 @@ export default function ssr( }) : []; - const reactive_declarations = component.reactive_declarations.map(_d => { - throw new Error('TODO'); - // let snippet = `[✂${d.node.body.start}-${d.node.end}✂]`; - - // if (d.declaration) { - // const declared = extract_names(d.declaration); - // const injected = declared.filter(name => { - // return name[0] !== '$' && component.var_lookup.get(name).injected; - // }); - - // const self_dependencies = injected.filter(name => d.dependencies.has(name)); - - // if (injected.length) { - // // in some cases we need to do `let foo; [expression]`, in - // // others we can do `let [expression]` - // const separate = ( - // self_dependencies.length > 0 || - // declared.length > injected.length - // ); - - // snippet = separate - // ? `let ${injected.join(', ')}; ${snippet}` - // : `let ${snippet}`; - // } - // } - - // return snippet; + const reactive_declarations = component.reactive_declarations.map(d => { + let snippet = b`${d.node}`; + + if (d.declaration) { + const declared = extract_names(d.declaration); + const injected = declared.filter(name => { + return name[0] !== '$' && component.var_lookup.get(name).injected; + }); + + const self_dependencies = injected.filter(name => d.dependencies.has(name)); + + if (injected.length) { + // in some cases we need to do `let foo; [expression]`, in + // others we can do `let [expression]` + const separate = ( + self_dependencies.length > 0 || + declared.length > injected.length + ); + + snippet = separate + ? b` + ${injected.map(name => b`let ${name};`)} + ${snippet}` + : b` + let ${snippet}`; + } + } + + return snippet; }); const main = renderer.has_bindings diff --git a/src/compiler/compile/utils/snip.ts b/src/compiler/compile/utils/snip.ts deleted file mode 100644 index 87163b18d9..0000000000 --- a/src/compiler/compile/utils/snip.ts +++ /dev/null @@ -1,4 +0,0 @@ -export function snip(expression) { - throw new Error(`snip bad`); - return `[✂${expression.node.start}-${expression.node.end}✂]`; -} \ No newline at end of file diff --git a/test/runtime/samples/_/_config.js b/test/runtime/samples/_/_config.js index ab18bd05d4..3258754400 100644 --- a/test/runtime/samples/_/_config.js +++ b/test/runtime/samples/_/_config.js @@ -1,20 +1,52 @@ export default { - html: `
Current state: deconflicted
+ +Current state: deconflicted
- elems = target.querySelectorAll('div'); - component.divs.forEach((e, i) => { - assert.equal(e, elems[i], `div ${i} is still correct`); - }); +Current state: {state}
+ +