104 lines
2.7 KiB

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';
import { stringify } from '../../../utils/stringify';
export default function visitComponent(
generator: SsrGenerator,
block: Block,
node: Node
) {
function stringifyAttribute(chunk: Node) {
if (chunk.type === 'Text') return chunk.data;
if (chunk.type === 'MustacheTag') {
const { snippet } = block.contextualise(chunk.expression);
return '${__escape( ' + snippet + ')}';
}
}
const attributes: Node[] = [];
const bindings: Node[] = [];
node.attributes.forEach((attribute: Node) => {
if (attribute.type === 'Attribute') {
attributes.push(attribute);
} else if (attribute.type === 'Binding') {
bindings.push(attribute);
}
});
const props = attributes
.map(attribute => {
let value;
if (attribute.value === true) {
value = `true`;
} else if (attribute.value.length === 0) {
value = `''`;
} else if (attribute.value.length === 1) {
const chunk = attribute.value[0];
if (chunk.type === 'Text') {
value = isNaN(chunk.data) ? stringify(chunk.data) : chunk.data;
} else {
const { snippet } = block.contextualise(chunk.expression);
value = snippet;
}
} else {
value = '`' + attribute.value.map(stringifyAttribute).join('') + '`';
}
return `${attribute.name}: ${value}`;
})
.concat(
bindings.map(binding => {
const { name } = getObject(binding.value);
const tail = binding.value.type === 'MemberExpression'
? getTailSnippet(binding.value)
: '';
const keypath = block.contexts.has(name)
? `${name}${tail}`
: `state.${name}${tail}`;
return `${binding.name}: ${keypath}`;
})
)
.join(', ');
const expression = node.name === ':Self' ? generator.name : `%components-${node.name}`;
bindings.forEach(binding => {
block.addBinding(binding, expression);
});
let open = `\${${expression}.render({${props}}`;
if (node.children.length) {
const appendTarget: AppendTarget = {
slots: { default: '' },
slotStack: ['default']
};
generator.appendTargets.push(appendTarget);
node.children.forEach((child: Node) => {
visit(generator, block, child);
});
const slotted = Object.keys(appendTarget.slots)
.map(name => `${name}: () => \`${appendTarget.slots[name]}\``)
.join(', ');
open += `, { slotted: { ${slotted} } }`;
generator.appendTargets.pop();
}
generator.append(open);
generator.append(')}');
}