diff --git a/src/compiler/compile/render_ssr/Renderer.ts b/src/compiler/compile/render_ssr/Renderer.ts index 37d6f3f5b2..ff91718437 100644 --- a/src/compiler/compile/render_ssr/Renderer.ts +++ b/src/compiler/compile/render_ssr/Renderer.ts @@ -13,7 +13,7 @@ import Text from './handlers/Text'; import Title from './handlers/Title'; import { AppendTarget, CompileOptions } from '../../interfaces'; import { INode } from '../nodes/interfaces'; -import { Expression } from 'estree'; +import { Expression, TemplateLiteral } from 'estree'; type Handler = (node: any, renderer: Renderer, options: CompileOptions) => void; @@ -45,22 +45,17 @@ export interface RenderOptions extends CompileOptions{ export default class Renderer { has_bindings = false; - state = { - quasi: { - type: 'TemplateElement', - value: { raw: '' } - } - }; - - literal = { - type: 'TemplateLiteral', - expressions: [], - quasis: [] - }; + stack: { current: { value: string }, literal: TemplateLiteral }[] = []; + current: { value: string }; + literal: TemplateLiteral; targets: AppendTarget[] = []; - append(code: string) { + constructor() { + this.push(); + } + + append() { throw new Error('no more append'); // if (this.targets.length) { // const target = this.targets[this.targets.length - 1]; @@ -72,17 +67,48 @@ export default class Renderer { } add_string(str: string) { - this.state.quasi.value.raw += str; + this.current.value += str; } add_expression(node: Expression) { - this.literal.quasis.push(this.state.quasi); + this.literal.quasis.push({ + type: 'TemplateElement', + value: { raw: this.current.value, cooked: null }, + tail: false + }); + this.literal.expressions.push(node); + this.current = { value: '' }; + } - this.state.quasi = { - type: 'TemplateElement', - value: { raw: '' } + push() { + const current = this.current = { value: '' }; + + const literal = this.literal = { + type: 'TemplateLiteral', + expressions: [], + quasis: [] }; + + this.stack.push({ current, literal }) + } + + pop() { + this.literal.quasis.push({ + type: 'TemplateElement', + value: { raw: this.current.value, cooked: null }, + tail: true + }); + + const popped = this.stack.pop(); + const last = this.stack[this.stack.length - 1]; + + if (last) { + this.literal = last.literal; + this.current = last.current; + } + + return popped.literal; } render(nodes: INode[], options: RenderOptions) { diff --git a/src/compiler/compile/render_ssr/handlers/EachBlock.ts b/src/compiler/compile/render_ssr/handlers/EachBlock.ts index ffe490922c..0d05836e82 100644 --- a/src/compiler/compile/render_ssr/handlers/EachBlock.ts +++ b/src/compiler/compile/render_ssr/handlers/EachBlock.ts @@ -1,29 +1,24 @@ -import { snip } from '../../utils/snip'; import Renderer, { RenderOptions } from '../Renderer'; import EachBlock from '../../nodes/EachBlock'; +import { x } from 'code-red'; export default function(node: EachBlock, renderer: Renderer, options: RenderOptions) { - const snippet = snip(node.expression); - - const { start, end } = node.context_node as any; - - const ctx = node.index - ? `([✂${start}-${end}✂], ${node.index})` - : `([✂${start}-${end}✂])`; - - const open = `\${${node.else ? `${snippet}.length ? ` : ''}@each(${snippet}, ${ctx} => \``; - renderer.append(open); + const args = [node.context_node]; + if (node.index) args.push({ type: 'Identifier', name: node.index }); + renderer.push(); renderer.render(node.children, options); + const result = renderer.pop(); - const close = `\`)`; - renderer.append(close); + const consequent = x`@each(${node.expression.node}, (${args}) => ${result})`; if (node.else) { - renderer.append(` : \``); + renderer.push(); renderer.render(node.else.children, options); - renderer.append(`\``); - } + const alternate = renderer.pop(); - renderer.append('}'); + renderer.add_expression(x`${node.expression.node}.length ? ${consequent} : ${alternate}`); + } else { + renderer.add_expression(consequent); + } } diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index 38addbfe4a..cd5a130bd2 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -143,7 +143,6 @@ export default function(node: Element, renderer: Renderer, options: RenderOption } else if (attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text') { const { name } = attribute; const snippet = attribute.chunks[0].node; - console.log(snippet); renderer.add_expression(x`@add_attribute("${name}", ${snippet}, ${boolean_attributes.has(name) ? 1 : 0})`); } else { renderer.add_string(` ${attribute.name}="`); diff --git a/src/compiler/compile/render_ssr/handlers/Head.ts b/src/compiler/compile/render_ssr/handlers/Head.ts index ecd32cc0ef..d457942922 100644 --- a/src/compiler/compile/render_ssr/handlers/Head.ts +++ b/src/compiler/compile/render_ssr/handlers/Head.ts @@ -1,10 +1,11 @@ import Renderer, { RenderOptions } from '../Renderer'; import Head from '../../nodes/Head'; +import { x } from 'code-red'; export default function(node: Head, renderer: Renderer, options: RenderOptions) { - renderer.append('${($$result.head += `'); - + renderer.push(); renderer.render(node.children, options); + const result = renderer.pop(); - renderer.append('`, "")}'); + renderer.add_expression(x`($$result.head += ${result}, "")`); } diff --git a/src/compiler/compile/render_ssr/handlers/HtmlTag.ts b/src/compiler/compile/render_ssr/handlers/HtmlTag.ts index ee62a4defc..2a46da2fc6 100644 --- a/src/compiler/compile/render_ssr/handlers/HtmlTag.ts +++ b/src/compiler/compile/render_ssr/handlers/HtmlTag.ts @@ -1,7 +1,6 @@ -import { snip } from '../../utils/snip'; import Renderer, { RenderOptions } from '../Renderer'; import RawMustacheTag from '../../nodes/RawMustacheTag'; export default function(node: RawMustacheTag, renderer: Renderer, _options: RenderOptions) { - renderer.append('${' + snip(node.expression) + '}'); + renderer.add_expression(node.expression.node); } diff --git a/src/compiler/compile/render_ssr/handlers/IfBlock.ts b/src/compiler/compile/render_ssr/handlers/IfBlock.ts index c841f1d593..504a237bc5 100644 --- a/src/compiler/compile/render_ssr/handlers/IfBlock.ts +++ b/src/compiler/compile/render_ssr/handlers/IfBlock.ts @@ -1,18 +1,17 @@ -import { snip } from '../../utils/snip'; import IfBlock from '../../nodes/IfBlock'; import Renderer, { RenderOptions } from '../Renderer'; -export default function(node: IfBlock, renderer: Renderer, options: RenderOptions) { - const snippet = snip(node.expression); +import { x } from 'code-red'; - renderer.append('${ ' + snippet + ' ? `'); +export default function(node: IfBlock, renderer: Renderer, options: RenderOptions) { + const condition = node.expression.node; + renderer.push(); renderer.render(node.children, options); + const consequent = renderer.pop(); - renderer.append('` : `'); - - if (node.else) { - renderer.render(node.else.children, options); - } + renderer.push(); + if (node.else) renderer.render(node.else.children, options); + const alternate = renderer.pop(); - renderer.append('` }'); + renderer.add_expression(x`${condition} ? ${consequent} : ${alternate}`); } diff --git a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts index 34a8055e98..acb9fb3257 100644 --- a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts +++ b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts @@ -81,29 +81,24 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend const slot_fns = []; if (node.children.length) { - const target: AppendTarget = { - slots: { default: '' }, - slot_stack: ['default'] - }; - - renderer.targets.push(target); - const slot_scopes = new Map(); - slot_scopes.set('default', get_slot_scope(node.lets)); + + renderer.push(); renderer.render(node.children, Object.assign({}, options, { slot_scopes })); - Object.keys(target.slots).forEach(name => { - const slot_scope = slot_scopes.get(name); + slot_scopes.set('default', { + input: get_slot_scope(node.lets), + output: renderer.pop() + }); + slot_scopes.forEach(({ input, output }, name) => { slot_fns.push( - `${quote_name_if_necessary(name)}: (${slot_scope}) => \`${target.slots[name]}\`` + p`${name}: (${input}) => ${output}` ); }); - - renderer.targets.pop(); } const slots = x`{ diff --git a/src/compiler/compile/render_ssr/handlers/Slot.ts b/src/compiler/compile/render_ssr/handlers/Slot.ts index 9eec56b359..fd0192526d 100644 --- a/src/compiler/compile/render_ssr/handlers/Slot.ts +++ b/src/compiler/compile/render_ssr/handlers/Slot.ts @@ -2,15 +2,20 @@ import { quote_prop_if_necessary } from '../../../utils/names'; import get_slot_data from '../../utils/get_slot_data'; import Renderer, { RenderOptions } from '../Renderer'; import Slot from '../../nodes/Slot'; +import { x } from 'code-red'; export default function(node: Slot, renderer: Renderer, options: RenderOptions) { - const prop = quote_prop_if_necessary(node.slot_name); - const slot_data = get_slot_data(node.values, true); - renderer.append(`\${$$slots${prop} ? $$slots${prop}(${slot_data}) : \``); - + console.group('push'); + renderer.push(); + console.groupEnd(); renderer.render(node.children, options); + const result = renderer.pop(); - renderer.append(`\`}`); + renderer.add_expression(x` + $$slots.${node.slot_name} + ? $$slots.${node.slot_name}(${slot_data}) + : ${result} + `); } diff --git a/src/compiler/compile/render_ssr/handlers/Title.ts b/src/compiler/compile/render_ssr/handlers/Title.ts index e5792ec81f..62d49d461a 100644 --- a/src/compiler/compile/render_ssr/handlers/Title.ts +++ b/src/compiler/compile/render_ssr/handlers/Title.ts @@ -2,9 +2,9 @@ import Renderer, { RenderOptions } from '../Renderer'; import Title from '../../nodes/Title'; export default function(node: Title, renderer: Renderer, options: RenderOptions) { - renderer.append(``); + renderer.add_string(`<title>`); renderer.render(node.children, options); - renderer.append(``); + renderer.add_string(``); } diff --git a/src/compiler/compile/render_ssr/handlers/shared/get_slot_scope.ts b/src/compiler/compile/render_ssr/handlers/shared/get_slot_scope.ts index 52aa70f015..bfde4cc195 100644 --- a/src/compiler/compile/render_ssr/handlers/shared/get_slot_scope.ts +++ b/src/compiler/compile/render_ssr/handlers/shared/get_slot_scope.ts @@ -1,6 +1,21 @@ import Let from '../../../nodes/Let'; +import { ObjectPattern } from 'estree'; -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(', ')} }`; +export function get_slot_scope(lets: Let[]): ObjectPattern { + if (lets.length === 0) return null; + + return { + type: 'ObjectPattern', + properties: lets.map(l => { + return { + type: 'Property', + kind: 'init', + method: false, + shorthand: false, + computed: false, + key: l.name, + value: l.value || l.name + }; + }) + }; } \ No newline at end of file diff --git a/src/compiler/compile/render_ssr/index.ts b/src/compiler/compile/render_ssr/index.ts index 9d190e6d88..e8a58f4477 100644 --- a/src/compiler/compile/render_ssr/index.ts +++ b/src/compiler/compile/render_ssr/index.ts @@ -20,7 +20,7 @@ export default function ssr( }, options)); // TODO put this inside the Renderer class - renderer.literal.quasis.push(renderer.state.quasi); + const literal = renderer.pop(); // TODO concatenate CSS maps const css = options.customElement ? @@ -108,7 +108,7 @@ export default function ssr( ${reactive_declarations} - $$rendered = ${renderer.literal}; + $$rendered = ${literal}; } while (!$$settled); return $$rendered; @@ -118,7 +118,7 @@ export default function ssr( ${reactive_declarations} - return ${renderer.literal};`; + return ${literal};`; const blocks = [ ...reactive_stores.map(({ name }) => {