move SSR logic into nodes

pull/1367/head
Rich Harris 7 years ago
parent 11d396f769
commit b2b3bda85a

@ -1,5 +1,5 @@
import deindent from '../../utils/deindent';
import { stringify } from '../../utils/stringify';
import { escape, escapeTemplate, stringify } from '../../utils/stringify';
import fixAttributeCasing from '../../utils/fixAttributeCasing';
import addToSet from '../../utils/addToSet';
import { DomGenerator } from '../dom/index';
@ -332,6 +332,18 @@ export default class Attribute extends Node {
);
});
}
stringifyForSsr() {
return this.chunks
.map((chunk: Node) => {
if (chunk.type === 'Text') {
return escapeTemplate(escape(chunk.data).replace(/"/g, '"'));
}
return '${__escape(' + chunk.snippet + ')}';
})
.join('');
}
}
// source: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes

@ -219,4 +219,24 @@ export default class AwaitBlock extends Node {
});
});
}
ssr(compiler, block) {
const { snippet } = this.expression;
const childBlock = block.child({});
compiler.append('${(function(__value) { if(__isPromise(__value)) return `');
this.pending.children.forEach((child: Node) => {
child.ssr(compiler, block);
});
compiler.append('`; return function(ctx) { return `');
this.then.children.forEach((child: Node) => {
child.ssr(compiler, block);
});
compiler.append(`\`;}(Object.assign({}, ctx, { ${this.value}: __value }));}(${snippet})) }`);
}
}

@ -8,4 +8,11 @@ export default class Comment extends Node {
super(compiler, parent, scope, info);
this.data = info.data;
}
ssr(compiler) {
// Allow option to preserve comments, otherwise ignore
if (compiler.options.preserveComments) {
compiler.append(`<!--${this.data}-->`);
}
}
}

@ -6,6 +6,7 @@ import CodeBuilder from '../../utils/CodeBuilder';
import getTailSnippet from '../../utils/getTailSnippet';
import getObject from '../../utils/getObject';
import quoteIfNecessary from '../../utils/quoteIfNecessary';
import { escape, escapeTemplate, stringify } from '../../utils/stringify';
import Node from './shared/Node';
import Block from '../dom/Block';
import Attribute from './Attribute';
@ -14,6 +15,7 @@ import mapChildren from './shared/mapChildren';
import Binding from './Binding';
import EventHandler from './EventHandler';
import Expression from './shared/Expression';
import { AppendTarget } from '../server-side-rendering/interfaces';
export default class Component extends Node {
type: 'Component';
@ -480,6 +482,106 @@ export default class Component extends Node {
remount(name: string) {
return `${this.var}._mount(${name}._slotted.default, null);`;
}
ssr(compiler, block) {
function stringifyAttribute(chunk: Node) {
if (chunk.type === 'Text') {
return escapeTemplate(escape(chunk.data));
}
return '${__escape( ' + chunk.snippet + ')}';
}
const bindingProps = this.bindings.map(binding => {
const { name } = getObject(binding.value.node);
const tail = binding.value.node.type === 'MemberExpression'
? getTailSnippet(binding.value.node)
: '';
return `${binding.name}: ctx.${name}${tail}`;
});
function getAttributeValue(attribute) {
if (attribute.isTrue) return `true`;
if (attribute.chunks.length === 0) return `''`;
if (attribute.chunks.length === 1) {
const chunk = attribute.chunks[0];
if (chunk.type === 'Text') {
return stringify(chunk.data);
}
return chunk.snippet;
}
return '`' + attribute.chunks.map(stringifyAttribute).join('') + '`';
}
const usesSpread = this.attributes.find(attr => attr.isSpread);
const props = usesSpread
? `Object.assign(${
this.attributes
.map(attribute => {
if (attribute.isSpread) {
return attribute.expression.snippet;
} else {
return `{ ${attribute.name}: ${getAttributeValue(attribute)} }`;
}
})
.concat(bindingProps.map(p => `{ ${p} }`))
.join(', ')
})`
: `{ ${this.attributes
.map(attribute => `${attribute.name}: ${getAttributeValue(attribute)}`)
.concat(bindingProps)
.join(', ')} }`;
const isDynamicComponent = this.name === 'svelte:component';
const expression = (
this.name === 'svelte:self' ? compiler.name :
isDynamicComponent ? `((${this.expression.snippet}) || __missingComponent)` :
`%components-${this.name}`
);
this.bindings.forEach(binding => {
block.addBinding(binding, expression);
});
let open = `\${${expression}._render(__result, ${props}`;
const options = [];
options.push(`store: options.store`);
if (this.children.length) {
const appendTarget: AppendTarget = {
slots: { default: '' },
slotStack: ['default']
};
compiler.appendTargets.push(appendTarget);
this.children.forEach((child: Node) => {
child.ssr(compiler, block);
});
const slotted = Object.keys(appendTarget.slots)
.map(name => `${name}: () => \`${appendTarget.slots[name]}\``)
.join(', ');
options.push(`slotted: { ${slotted} }`);
compiler.appendTargets.pop();
}
if (options.length) {
open += `, { ${options.join(', ')} }`;
}
compiler.append(open);
compiler.append(')}');
}
}
function isComputed(node: Node) {

@ -472,4 +472,37 @@ export default class EachBlock extends Node {
// TODO consider keyed blocks
return `for (var #i = 0; #i < ${this.iterations}.length; #i += 1) ${this.iterations}[#i].m(${name}._slotted.default, null);`;
}
ssr(compiler, block) {
const { snippet } = this.expression;
const props = [`${this.context}: item`]
.concat(this.destructuredContexts.map((name, i) => `${name}: item[${i}]`));
const getContext = this.index
? `(item, i) => Object.assign({}, ctx, { ${props.join(', ')}, ${this.index}: i })`
: `item => Object.assign({}, ctx, { ${props.join(', ')} })`;
const open = `\${ ${this.else ? `${snippet}.length ? ` : ''}__each(${snippet}, ${getContext}, ctx => \``;
compiler.append(open);
const childBlock = block.child({});
this.children.forEach((child: Node) => {
child.ssr(compiler, childBlock);
});
const close = `\`)`;
compiler.append(close);
if (this.else) {
compiler.append(` : \``);
this.else.children.forEach((child: Node) => {
child.ssr(compiler, block);
});
compiler.append(`\``);
}
compiler.append('}');
}
}

@ -17,6 +17,9 @@ import Text from './Text';
import * as namespaces from '../../utils/namespaces';
import mapChildren from './shared/mapChildren';
// source: https://gist.github.com/ArjanSchouten/0b8574a6ad7f5065a5e7
const booleanAttributes = new Set('async autocomplete autofocus autoplay border challenge checked compact contenteditable controls default defer disabled formnovalidate frameborder hidden indeterminate ismap loop multiple muted nohref noresize noshade novalidate nowrap open readonly required reversed scoped scrolling seamless selected sortable spellcheck translate'.split(' '));
export default class Element extends Node {
type: 'Element';
name: string;
@ -827,6 +830,86 @@ export default class Element extends Node {
);
}
}
ssr(compiler, block) {
let openingTag = `<${this.name}`;
let textareaContents; // awkward special case
const slot = this.getStaticAttributeValue('slot');
if (slot && this.hasAncestor('Component')) {
const slot = this.attributes.find((attribute: Node) => attribute.name === 'slot');
const slotName = slot.chunks[0].data;
const appendTarget = compiler.appendTargets[compiler.appendTargets.length - 1];
appendTarget.slotStack.push(slotName);
appendTarget.slots[slotName] = '';
}
if (this.attributes.find(attr => attr.isSpread)) {
// TODO dry this out
const args = [];
this.attributes.forEach(attribute => {
if (attribute.isSpread) {
args.push(attribute.expression.snippet);
} else {
if (attribute.name === 'value' && this.name === 'textarea') {
textareaContents = attribute.stringifyForSsr();
} else if (attribute.isTrue) {
args.push(`{ ${quoteIfNecessary(attribute.name)}: true }`);
} else if (
booleanAttributes.has(attribute.name) &&
attribute.chunks.length === 1 &&
attribute.chunks[0].type !== 'Text'
) {
// a boolean attribute with one non-Text chunk
args.push(`{ ${quoteIfNecessary(attribute.name)}: ${attribute.chunks[0].snippet} }`);
} else {
args.push(`{ ${quoteIfNecessary(attribute.name)}: \`${attribute.stringifyForSsr()}\` }`);
}
}
});
openingTag += "${__spread([" + args.join(', ') + "])}";
} else {
this.attributes.forEach((attribute: Node) => {
if (attribute.type !== 'Attribute') return;
if (attribute.name === 'value' && this.name === 'textarea') {
textareaContents = attribute.stringifyForSsr();
} else if (attribute.isTrue) {
openingTag += ` ${attribute.name}`;
} else if (
booleanAttributes.has(attribute.name) &&
attribute.chunks.length === 1 &&
attribute.chunks[0].type !== 'Text'
) {
// a boolean attribute with one non-Text chunk
openingTag += '${' + attribute.chunks[0].snippet + ' ? " ' + attribute.name + '" : "" }';
} else {
openingTag += ` ${attribute.name}="${attribute.stringifyForSsr()}"`;
}
});
}
if (this._cssRefAttribute) {
openingTag += ` svelte-ref-${this._cssRefAttribute}`;
}
openingTag += '>';
compiler.append(openingTag);
if (this.name === 'textarea' && textareaContents !== undefined) {
compiler.append(textareaContents);
} else {
this.children.forEach((child: Node) => {
child.ssr(compiler, block);
});
}
if (!isVoidElementName(this.name)) {
compiler.append(`</${this.name}>`);
}
}
}
function getRenderStatement(

@ -35,4 +35,14 @@ export default class Head extends Node {
child.build(block, 'document.head', null);
});
}
ssr(compiler, block) {
compiler.append('${(__result.head += `');
this.children.forEach((child: Node) => {
child.ssr(compiler, block);
});
compiler.append('`, "")}');
}
}

@ -476,6 +476,30 @@ export default class IfBlock extends Node {
return branches;
}
ssr(compiler, block) {
const { snippet } = this.expression;
compiler.append('${ ' + snippet + ' ? `');
const childBlock = block.child({
conditions: block.conditions.concat(snippet),
});
this.children.forEach((child: Node) => {
child.ssr(compiler, childBlock);
});
compiler.append('` : `');
if (this.else) {
this.else.children.forEach((child: Node) => {
child.ssr(compiler, block);
});
}
compiler.append('` }');
}
visitChildren(block: Block, node: Node) {
node.children.forEach((child: Node) => {
child.build(node.block, null, 'nodes');

@ -24,4 +24,14 @@ export default class MustacheTag extends Tag {
remount(name: string) {
return `@appendNode(${this.var}, ${name}._slotted.default);`;
}
ssr(compiler) {
compiler.append(
this.parent &&
this.parent.type === 'Element' &&
this.parent.name === 'style'
? '${' + this.expression.snippet + '}'
: '${__escape(' + this.expression.snippet + ')}'
);
}
}

@ -87,4 +87,8 @@ export default class RawMustacheTag extends Tag {
remount(name: string) {
return `@appendNode(${this.var}, ${name}._slotted.default);`;
}
ssr(compiler) {
compiler.append('${' + this.expression.snippet + '}');
}
}

@ -150,4 +150,17 @@ export default class Slot extends Element {
return null;
}
ssr(compiler, block) {
const name = this.attributes.find(attribute => attribute.name === 'name');
const slotName = name && name.chunks[0].data || 'default';
compiler.append(`\${options && options.slotted && options.slotted.${slotName} ? options.slotted.${slotName}() : \``);
this.children.forEach((child: Node) => {
child.ssr(compiler, block);
});
compiler.append(`\`}`);
}
}

@ -1,4 +1,4 @@
import { stringify } from '../../utils/stringify';
import { escape, escapeHTML, escapeTemplate, stringify } from '../../utils/stringify';
import Node from './shared/Node';
import Block from '../dom/Block';
@ -67,4 +67,17 @@ export default class Text extends Node {
remount(name: string) {
return `@appendNode(${this.var}, ${name}._slotted.default);`;
}
ssr(compiler) {
let text = this.data;
if (
!this.parent ||
this.parent.type !== 'Element' ||
(this.parent.name !== 'script' && this.parent.name !== 'style')
) {
// unless this Text node is inside a <script> or <style> element, escape &,<,>
text = escapeHTML(text);
}
compiler.append(escape(escapeTemplate(text)));
}
}

@ -101,4 +101,14 @@ export default class Title extends Node {
block.builders.hydrate.addLine(`document.title = ${value};`);
}
}
ssr(compiler, block) {
compiler.append(`<title>`);
this.children.forEach((child: Node) => {
child.ssr(compiler, block);
});
compiler.append(`</title>`);
}
}

@ -217,4 +217,8 @@ export default class Window extends Node {
`);
}
}
ssr() {
// noop
}
}

@ -63,7 +63,7 @@ export default function ssr(
});
trim(generator.fragment.children).forEach((node: Node) => {
visit(generator, mainBlock, node);
node.ssr(generator, mainBlock);
});
const css = generator.customElement ?

@ -1,13 +0,0 @@
import visitors from './visitors/index';
import { SsrGenerator } from './index';
import Block from './Block';
import { Node } from '../../interfaces';
export default function visit(
generator: SsrGenerator,
block: Block,
node: Node
) {
const visitor = visitors[node.type];
visitor(generator, block, node);
}

@ -1,28 +0,0 @@
import visit from '../visit';
import { SsrGenerator } from '../index';
import Block from '../Block';
import { Node } from '../../../interfaces';
export default function visitAwaitBlock(
generator: SsrGenerator,
block: Block,
node: Node
) {
const { snippet } = node.expression;
const childBlock = block.child({});
generator.append('${(function(__value) { if(__isPromise(__value)) return `');
node.pending.children.forEach((child: Node) => {
visit(generator, childBlock, child);
});
generator.append('`; return function(ctx) { return `');
node.then.children.forEach((child: Node) => {
visit(generator, childBlock, child);
});
generator.append(`\`;}(Object.assign({}, ctx, { ${node.value}: __value }));}(${snippet})) }`);
}

@ -1,14 +0,0 @@
import { SsrGenerator } from '../index';
import Block from '../Block';
import { Node } from '../../../interfaces';
export default function visitComment(
generator: SsrGenerator,
block: Block,
node: Node
) {
// Allow option to preserve comments, otherwise ignore
if (generator && generator.options && generator.options.preserveComments) {
generator.append(`<!--${node.data}-->`);
}
}

@ -1,113 +0,0 @@
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 { escape, escapeTemplate, stringify } from '../../../utils/stringify';
export default function visitComponent(
generator: SsrGenerator,
block: Block,
node: Node
) {
function stringifyAttribute(chunk: Node) {
if (chunk.type === 'Text') {
return escapeTemplate(escape(chunk.data));
}
return '${__escape( ' + chunk.snippet + ')}';
}
const bindingProps = node.bindings.map(binding => {
const { name } = getObject(binding.value.node);
const tail = binding.value.node.type === 'MemberExpression'
? getTailSnippet(binding.value.node)
: '';
return `${binding.name}: ctx.${name}${tail}`;
});
function getAttributeValue(attribute) {
if (attribute.isTrue) return `true`;
if (attribute.chunks.length === 0) return `''`;
if (attribute.chunks.length === 1) {
const chunk = attribute.chunks[0];
if (chunk.type === 'Text') {
return stringify(chunk.data);
}
return chunk.snippet;
}
return '`' + attribute.chunks.map(stringifyAttribute).join('') + '`';
}
const usesSpread = node.attributes.find(attr => attr.isSpread);
const props = usesSpread
? `Object.assign(${
node.attributes
.map(attribute => {
if (attribute.isSpread) {
return attribute.expression.snippet;
} else {
return `{ ${attribute.name}: ${getAttributeValue(attribute)} }`;
}
})
.concat(bindingProps.map(p => `{ ${p} }`))
.join(', ')
})`
: `{ ${node.attributes
.map(attribute => `${attribute.name}: ${getAttributeValue(attribute)}`)
.concat(bindingProps)
.join(', ')} }`;
const isDynamicComponent = node.name === 'svelte:component';
const expression = (
node.name === 'svelte:self' ? generator.name :
isDynamicComponent ? `((${node.expression.snippet}) || __missingComponent)` :
`%components-${node.name}`
);
node.bindings.forEach(binding => {
block.addBinding(binding, expression);
});
let open = `\${${expression}._render(__result, ${props}`;
const options = [];
options.push(`store: options.store`);
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(', ');
options.push(`slotted: { ${slotted} }`);
generator.appendTargets.pop();
}
if (options.length) {
open += `, { ${options.join(', ')} }`;
}
generator.append(open);
generator.append(')}');
}

@ -1,41 +0,0 @@
import visit from '../visit';
import { SsrGenerator } from '../index';
import Block from '../Block';
import { Node } from '../../../interfaces';
export default function visitEachBlock(
generator: SsrGenerator,
block: Block,
node: Node
) {
const { snippet } = node.expression;
const props = [`${node.context}: item`]
.concat(node.destructuredContexts.map((name, i) => `${name}: item[${i}]`));
const getContext = node.index
? `(item, i) => Object.assign({}, ctx, { ${props.join(', ')}, ${node.index}: i })`
: `item => Object.assign({}, ctx, { ${props.join(', ')} })`;
const open = `\${ ${node.else ? `${snippet}.length ? ` : ''}__each(${snippet}, ${getContext}, ctx => \``;
generator.append(open);
const childBlock = block.child({});
node.children.forEach((child: Node) => {
visit(generator, childBlock, child);
});
const close = `\`)`;
generator.append(close);
if (node.else) {
generator.append(` : \``);
node.else.children.forEach((child: Node) => {
visit(generator, block, child);
});
generator.append(`\``);
}
generator.append('}');
}

@ -1,103 +0,0 @@
import visitComponent from './Component';
import visitSlot from './Slot';
import isVoidElementName from '../../../utils/isVoidElementName';
import quoteIfNecessary from '../../../utils/quoteIfNecessary';
import visit from '../visit';
import { SsrGenerator } from '../index';
import Element from '../../nodes/Element';
import Block from '../Block';
import { Node } from '../../../interfaces';
import stringifyAttributeValue from './shared/stringifyAttributeValue';
import { escape } from '../../../utils/stringify';
// source: https://gist.github.com/ArjanSchouten/0b8574a6ad7f5065a5e7
const booleanAttributes = new Set('async autocomplete autofocus autoplay border challenge checked compact contenteditable controls default defer disabled formnovalidate frameborder hidden indeterminate ismap loop multiple muted nohref noresize noshade novalidate nowrap open readonly required reversed scoped scrolling seamless selected sortable spellcheck translate'.split(' '));
export default function visitElement(
generator: SsrGenerator,
block: Block,
node: Element
) {
if (node.name === 'slot') {
visitSlot(generator, block, node);
return;
}
let openingTag = `<${node.name}`;
let textareaContents; // awkward special case
const slot = node.getStaticAttributeValue('slot');
if (slot && node.hasAncestor('Component')) {
const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot');
const slotName = slot.chunks[0].data;
const appendTarget = generator.appendTargets[generator.appendTargets.length - 1];
appendTarget.slotStack.push(slotName);
appendTarget.slots[slotName] = '';
}
if (node.attributes.find(attr => attr.isSpread)) {
// TODO dry this out
const args = [];
node.attributes.forEach(attribute => {
if (attribute.isSpread) {
args.push(attribute.expression.snippet);
} else {
if (attribute.name === 'value' && node.name === 'textarea') {
textareaContents = stringifyAttributeValue(block, attribute.chunks);
} else if (attribute.isTrue) {
args.push(`{ ${quoteIfNecessary(attribute.name)}: true }`);
} else if (
booleanAttributes.has(attribute.name) &&
attribute.chunks.length === 1 &&
attribute.chunks[0].type !== 'Text'
) {
// a boolean attribute with one non-Text chunk
args.push(`{ ${quoteIfNecessary(attribute.name)}: ${attribute.chunks[0].snippet} }`);
} else {
args.push(`{ ${quoteIfNecessary(attribute.name)}: \`${stringifyAttributeValue(block, attribute.chunks)}\` }`);
}
}
});
openingTag += "${__spread([" + args.join(', ') + "])}";
} else {
node.attributes.forEach((attribute: Node) => {
if (attribute.type !== 'Attribute') return;
if (attribute.name === 'value' && node.name === 'textarea') {
textareaContents = stringifyAttributeValue(block, attribute.chunks);
} else if (attribute.isTrue) {
openingTag += ` ${attribute.name}`;
} else if (
booleanAttributes.has(attribute.name) &&
attribute.chunks.length === 1 &&
attribute.chunks[0].type !== 'Text'
) {
// a boolean attribute with one non-Text chunk
openingTag += '${' + attribute.chunks[0].snippet + ' ? " ' + attribute.name + '" : "" }';
} else {
openingTag += ` ${attribute.name}="${stringifyAttributeValue(block, attribute.chunks)}"`;
}
});
}
if (node._cssRefAttribute) {
openingTag += ` svelte-ref-${node._cssRefAttribute}`;
}
openingTag += '>';
generator.append(openingTag);
if (node.name === 'textarea' && textareaContents !== undefined) {
generator.append(textareaContents);
} else {
node.children.forEach((child: Node) => {
visit(generator, block, child);
});
}
if (!isVoidElementName(node.name)) {
generator.append(`</${node.name}>`);
}
}

@ -1,19 +0,0 @@
import { SsrGenerator } from '../index';
import Block from '../Block';
import { Node } from '../../../interfaces';
import stringifyAttributeValue from './shared/stringifyAttributeValue';
import visit from '../visit';
export default function visitDocument(
generator: SsrGenerator,
block: Block,
node: Node
) {
generator.append('${(__result.head += `');
node.children.forEach((child: Node) => {
visit(generator, block, child);
});
generator.append('`, "")}');
}

@ -1,32 +0,0 @@
import visit from '../visit';
import { SsrGenerator } from '../index';
import Block from '../Block';
import { Node } from '../../../interfaces';
export default function visitIfBlock(
generator: SsrGenerator,
block: Block,
node: Node
) {
const { snippet } = node.expression;
generator.append('${ ' + snippet + ' ? `');
const childBlock = block.child({
conditions: block.conditions.concat(snippet),
});
node.children.forEach((child: Node) => {
visit(generator, childBlock, child);
});
generator.append('` : `');
if (node.else) {
node.else.children.forEach((child: Node) => {
visit(generator, childBlock, child);
});
}
generator.append('` }');
}

@ -1,19 +0,0 @@
import { SsrGenerator } from '../index';
import Block from '../Block';
import { Node } from '../../../interfaces';
export default function visitMustacheTag(
generator: SsrGenerator,
block: Block,
node: Node
) {
const { snippet } = node.expression;
generator.append(
node.parent &&
node.parent.type === 'Element' &&
node.parent.name === 'style'
? '${' + snippet + '}'
: '${__escape(' + snippet + ')}'
);
}

@ -1,13 +0,0 @@
import { SsrGenerator } from '../index';
import Block from '../Block';
import { Node } from '../../../interfaces';
export default function visitRawMustacheTag(
generator: SsrGenerator,
block: Block,
node: Node
) {
const { snippet } = node.expression;
generator.append('${' + snippet + '}');
}

@ -1,21 +0,0 @@
import visit from '../visit';
import { SsrGenerator } from '../index';
import Block from '../Block';
import { Node } from '../../../interfaces';
export default function visitSlot(
generator: SsrGenerator,
block: Block,
node: Node
) {
const name = node.attributes.find((attribute: Node) => attribute.name);
const slotName = name && name.chunks[0].data || 'default';
generator.append(`\${options && options.slotted && options.slotted.${slotName} ? options.slotted.${slotName}() : \``);
node.children.forEach((child: Node) => {
visit(generator, block, child);
});
generator.append(`\`}`);
}

@ -1,21 +0,0 @@
import { SsrGenerator } from '../index';
import Block from '../Block';
import { escape, escapeHTML, escapeTemplate } from '../../../utils/stringify';
import { Node } from '../../../interfaces';
export default function visitText(
generator: SsrGenerator,
block: Block,
node: Node
) {
let text = node.data;
if (
!node.parent ||
node.parent.type !== 'Element' ||
(node.parent.name !== 'script' && node.parent.name !== 'style')
) {
// unless this Text node is inside a <script> or <style> element, escape &,<,>
text = escapeHTML(text);
}
generator.append(escape(escapeTemplate(text)));
}

@ -1,19 +0,0 @@
import { SsrGenerator } from '../index';
import Block from '../Block';
import { escape } from '../../../utils/stringify';
import visit from '../visit';
import { Node } from '../../../interfaces';
export default function visitTitle(
generator: SsrGenerator,
block: Block,
node: Node
) {
generator.append(`<title>`);
node.children.forEach((child: Node) => {
visit(generator, block, child);
});
generator.append(`</title>`);
}

@ -1,3 +0,0 @@
export default function visitWindow() {
// noop
}

@ -1,29 +0,0 @@
import AwaitBlock from './AwaitBlock';
import Comment from './Comment';
import Component from './Component';
import EachBlock from './EachBlock';
import Element from './Element';
import Head from './Head';
import IfBlock from './IfBlock';
import MustacheTag from './MustacheTag';
import RawMustacheTag from './RawMustacheTag';
import Slot from './Slot';
import Text from './Text';
import Title from './Title';
import Window from './Window';
export default {
AwaitBlock,
Comment,
Component,
EachBlock,
Element,
Head,
IfBlock,
MustacheTag,
RawMustacheTag,
Slot,
Text,
Title,
Window
};

@ -1,15 +0,0 @@
import Block from '../../Block';
import { escape, escapeTemplate } from '../../../../utils/stringify';
import { Node } from '../../../../interfaces';
export default function stringifyAttributeValue(block: Block, chunks: Node[]) {
return chunks
.map((chunk: Node) => {
if (chunk.type === 'Text') {
return escapeTemplate(escape(chunk.data).replace(/"/g, '&quot;'));
}
return '${__escape(' + chunk.snippet + ')}';
})
.join('');
}
Loading…
Cancel
Save